Welcome to documentation for XLED - Smart LED Christmas lights!¶
Contents:
XLED - unofficial control of Twinkly - Smart Decoration LED lights¶
XLED is a python library and command line interface (CLI) to control Twinkly - Smart Decoration LED lights for Christmas.
Official materials says:
Twinkly is a LED light device that you can control via smartphone. It allows you to play with colouful and animated effects, or create new ones. Decoration lights, not suitable for household illumination.
Since its Kickstarter project in 2016 many products were introduced with varying properties and features. Most notably products released since September 2019 are identified as Generation II. Older products are since then referred as Generation I.
Library and CLI are free software available under MIT license.
Installation¶
Both library and CLI tool are supported on Linux, primarily Fedora.
- First make sure that you have pip installed. E.g. for Fedora:
- You might want to create and activate a virtual environment. E.g.:
- Install xled from PyPI:
Usage¶
If you have installed the project into virtual environment, activate it first. E.g.
Use of the library:
>>> import xled
>>> discovered_device = xled.discover.discover()
>>> discovered_device.id
'Twinkly_33AAFF'
>>> control = xled.ControlInterface(discovered_device.ip_address, discovered_device.hw_address)
>>> control.set_mode('movie')
<ApplicationResponse [1000]>
>>> control.get_mode()['mode']
'movie'
>>> control.get_device_info()['number_of_led']
210
Documentation for the library can be found online.
Use of the CLI:
$ xled on
Looking for any device...
Working on device: Twinkly_33AAFF
Turned on.
For more commands and options see xled –help.
Why?¶
My first Twinkly was 105 LEDs starter light set. That was the latest available model in 2017: TW105S-EU. As of December 2017 there are only two ways to control lights: mobile app on Android or iOS or hardware button on the cord.
Android application didn’t work as advertised on my Xiaomi Redmi 3S phone. On first start it connected and disconnected in very fast pace (like every 1-2 seconds) to the hardware. I wasn’t able to control anything at all. Later I wanted to connect it to my local WiFi network. But popup dialog that shouldn’t have appear never did so.
Public API was promised around Christmas 2016 for next season. Later update from October 2016 it seems API won’t be available any time soon:
API for external control are on our dev check list, we definitely need some feedback from the community to understand which could be a proper core set to start with.
It turned out that application uses HTTP to control lights. I ended up with capturing network traffic and documented this private API. In the end I’m able to configure the device pretty easilly.
As of 2020 Twinkly devices can be controlled by Amazon Alexa and Google Assistant as well. Mobile application now requires an account to operate lights even locally. No sign of public API for local devices though. Therefore with my second device - Twinkly 210 RGB+W Wall I keep improving this library and CLI documentation to be able to operate my devices locally and not rely on availability of manufacturer’s servers.
References¶
Unofficial documentation of private protocol and API is available online.
There are other projects that might be more suitable for your needs:
- Twinkly integration in Home Assistant
- SmartThings:
- TwinklyTree Binding for openHAB
- Twinkly HomeKit Hub for Mongoose OS using Twinkly library for Mongoose OS
- TwinklyWPF - .net 5 GUI and API library
- ioBroker.twinkly - twinkly adapter for ioBroker to communicate with the Twinkly lights
- Twinkly.vb for HomeSeer
- thingzi-logic-twinkly - Twinkly lights integration for node red
- Python class to interact with generation I device and IDA Pro loader of firmware binary in Twinkly Twinkly Little Star by F-Secure LABS.
Credits¶
This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.
Support¶
Supported Operating Systems¶
Generally both CLI and library is supported only on Linux. Most often code is tested supported Fedora and CI usually runs on Ubuntu.
It might work on other platforms where python runs but there is no guarantee. No special code for other platforms will be added.
Supported Python Versions¶
Both CLI and library are written in python.
Python 2¶
Project supports python 2.7. Python 2 will be supported as long as libraries project depends on are reasonably available and possible to support with python 3 as well.
Python 3¶
Following versions of python 3 are supported:
- 3.6
- 3.7
- 3.8
- 3.9
- 3.10
Python 3 support will more or less follow python 3 lifecycle - if new version is released project should run on it as well. As python version goes end of life from upstream the support by the project will fade away as well. See Status of Python branches for upstream lifecycle.
Supported Devices¶
Generally device models that are listed in Hardware section of xled-docs are supported. Other models might work but there is no guarantee.
Command Line Interface¶
CLI Guide¶
Primary goal of command line interface is to provide high level way to query and control devices. It provides subset of features that devices are capable of. Tool doesn’t keep any configuration and before each operation it discovers available devices. By default first device that responds is being controlled. Alternatively specified device can be controlled.
Run xled –help to get available options.
Python API Documentation / Guide¶
xled package¶
Submodules¶
xled.auth module¶
xled.auth¶
Custom authentication handler and authenticated session to be used with
requests.Session
-
class
xled.auth.
BaseUrlChallengeResponseAuthSession
(hw_address=None, client=None, auto_refresh_token=True, **kwargs)[source]¶ Bases:
requests_toolbelt.sessions.BaseUrlSession
Extension to
requests_toolbelt.BaseUrlSession
to provide authentication.Any request used with this session gets authentication token added. Authentication token can be fetched even separately.
-
access_token
¶ Current authentication token if exists. None if it wasn’t fetched yet.
Returns headers with added authorization
Parameters: headers (dict) – user supplied request headers Return type: dict
-
add_token
(headers=None)[source]¶ Adds token header to dictionary with headers
Parameters: headers (dict) – Optional initial dictionary with headers. Returns: Dict with added authentication header. Return type: dict Raises: TokenExpiredError – If token is expected to be expired.
Boolean that indicates whether this session has an ChallengeResponse token or not. If self.authorized is True, you can reasonably expect ChallengeResponse-protected requests to the resource to succeed. If self.authorized is False, you need the user to go through the ChallengeResponse authentication dance before ChallengeResponse-protected requests to the resource will succeed. :rtype: bool
-
challenge_url
¶ Full URL of login endpoint
Returns: String with full url Return type: str
-
fetch_token
()[source]¶ Main authentication method that fetches new token
Returns: Token as string. Return type: str
-
prepare_request_challenge
()[source]¶ Creates prepared request to send challenge
Returns: prepared request Return type: requests.PreparedRequest
-
prepare_request_verify
()[source]¶ Creates prepared request to send verification
Returns: prepared request Return type: requests.PreparedRequest
-
request
(method, url, headers=None, withhold_token=False, **kwargs)[source]¶ Main request method of the session
Adds authentication to method from
requests_toolbelt.BaseUrlSession
. Takes auto_refresh_token in mind.Parameters: withhold_token (dict) – If boolean is True authentication token isn’t added to the request. Return type: requests.Response
-
verify_url
¶ Full URL of verify endpoint
Returns: Full URL. Return type: str
-
-
class
xled.auth.
ChallengeResponseAuth
(login_url, verify_url, hw_address=None)[source]¶ Bases:
requests.auth.AuthBase
-
handle_401
(response, **kwargs)[source]¶ Handles 401’s, attempts to use challenge-response authentication
-
-
class
xled.auth.
ClientApplication
(challenge=None)[source]¶ Bases:
xled.auth.ValidatingClientMixin
-
parse_response_challenge
(response, **kwargs)[source]¶ Modifies prepared request so challenge can be sent to login
Param: requests.PreparedRequest response prepared request Returns: Modified prepared request Return type: requests.PreparedRequest Raises: AuthenticationError – if application response isn’t valid
-
parse_response_verify
(response, **kwargs)[source]¶ Process response from verify call
This is last step to be able to use token to authenticate.
Param: requests.Response response Response to process. Returns: Same response that was used as parameter Return type: requests.Response Raises: AuthenticationError – if application response isn’t valid
-
populate_token_attributes
(response)[source]¶ Fetches token attributes from application response
Param: app_response response Response from login endpoint. Type: application_response ApplicationResponse
-
prepare_request_challenge
(request)[source]¶ Modifies prepared request so it can be sent to login
Param: requests.PreparedRequest request prepared request to modify Returns: Modified prepared request Return type: requests.PreparedRequest
-
prepare_request_verify
(request)[source]¶ Modifies prepared request so it can be sent to verify challenge
Param: requests.PreparedRequest request prepared request to modify Returns: Modified prepared request Return type: requests.PreparedRequest
-
token_expired
¶
-
token_valid
¶
-
-
class
xled.auth.
ValidatingClientMixin
[source]¶ Bases:
object
Mixin adds functionality to
ClientApplication
to authenticate server-
challenge_response_valid
(hw_address=None)[source]¶ Verifies server with hardware address returned correct challenge response
Creates challenge-response for server’s hardware address, challenge and shared password and compares it with stored challenge-response.
Parameters: hw_address (str) – Hardware address of a server. Returns: If challenge-response is valid returns True. If it cannot be verified returns None. Return type: bool or None Raises: ValidationError – if chalenge-response is invalid
-
xled.cli module¶
Console script for xled.
xled.compat module¶
xled.control module¶
xled.control¶
This module contains interface to control specific device
See also
- RESTful API reference
- for more details about API that is used by the application.
- Protocol details
- for various operations.
-
class
xled.control.
ControlInterface
(host, hw_address=None)[source]¶ Bases:
object
Main interface to control specific device
Parameters: host (str) – Hostname (or IP address) of a device to control -
base_url
¶
-
check_status
()[source]¶ Checks that the device is online and responding
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
delete_movies
()[source]¶ Remove all uploaded movies.
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
delete_playlist
()[source]¶ Clears the playlist
See also
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
firmware_0_update
(firmware)[source]¶ Uploads first stage of the firmware
Parameters: firmware – file-like object that points to firmware file. Raises: ApplicationError – on application error Return type: ApplicationResponse
-
firmware_1_update
(firmware)[source]¶ Uploads second stage of the firmware
Parameters: firmware – file-like object that points to firmware file. Raises: ApplicationError – on application error Return type: ApplicationResponse
-
firmware_update
(stage0_sha1sum, stage1_sha1sum=None)[source]¶ Performs firmware update from previously uploaded images
Parameters: - stage0_sha1sum (str) – SHA1 digest of first stage
- stage1_sha1sum (str) – SHA1 digest of second stage
Raises: ApplicationError – on application error
Return type:
-
firmware_version
()[source]¶ Gets firmware version
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_brightness
()[source]¶ Gets current brightness level and if dimming is applied
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_device_info
()[source]¶ Gets detailed information about device
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_device_name
()[source]¶ Gets device name.
See also
Raises: ApplicationError – on application error Returns: current device name. Return type: ApplicationResponse
-
get_led_config
()[source]¶ Gets the structural configuration of the leds in term of strings
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_led_effects
()[source]¶ Gets the number of effects and their unique_ids
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_led_effects_current
()[source]¶ Gets the current effect index
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_led_layout
()[source]¶ Gets the physical layout of the leds
See also
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_led_movie_config
()[source]¶ Gets the parameters for playing the uploaded movie
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_mode
()[source]¶ Gets current LED operation mode.
See also
set_mode()
to set modes.Raises: ApplicationError – on application error Returns: current LED operation mode. See set_mode()
for possible return values.Return type: ApplicationResponse
-
get_movies
()[source]¶ Gets list of uploaded movies.
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_movies_current
()[source]¶ Gets the movie id of the currently played movie in the movie list
See also
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_mqtt_config
()[source]¶ Gets the mqtt configuration parameters
See also
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_network_status
()[source]¶ Gets network status
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_playlist
()[source]¶ Gets the current playlist
See also
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_playlist_current
()[source]¶ Gets the movie id of the currently played movie in the playlist
See also
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_saturation
()[source]¶ Gets current saturation level and if desaturation is applied
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
get_timer
()[source]¶ Gets current timer
Raises: ApplicationError – on application error Returns: {time_on, time_off, time_now}. See set_timer()
for explanation of return values.Return type: ApplicationResponse
-
led_reset
()[source]¶ Resets LED
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
network_scan
()[source]¶ Initiate WiFi network scan
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
network_scan_results
()[source]¶ Get results of WiFi network scan
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
session
¶ Session object to operate on
Returns: session object with auth BaseUrlChallengeResponseAuthSession()
.Return type: requests.Session
-
set_brightness
(brightness=None, enabled=True, relative=False)[source]¶ Sets new brightness or enable/disable brightness dimming
Parameters: - brightness – new brightness in range of 0..100 or a relative change in -100..100 or None if no change is requested
- enabled (bool) – set to False if no dimming should be applied
- relative (bool) – set to True to make a relative change
Raises: ApplicationError – on application error
Return type:
-
set_device_name
(name)[source]¶ Sets new device name
Parameters: name (str) – new device name Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_led_effects_current
(effect_id)[source]¶ Sets the current effect of effect mode
Parameters: effect_id (int) – id of effect Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_led_layout
(source, coordinates, synthesized=False)[source]¶ Sets the physical layout of the leds
Parameters: - source (str) – 2d, 3d, or linear
- coordinates (list) – list of dictionaries with keys ‘x’, ‘y’, and ‘z’
- synthesized (bool) – presumably whether it is synthetic or real coordinates
Raises: ApplicationError – on application error
Return type:
-
set_led_movie_config
(frame_delay, frames_number, leds_number)[source]¶ Sets movie configuration for the last uploaded movie
Parameters: - frame_delay (int) – speed of movie (delay between frames in ms)
- leds_number (int) – total number of LEDs
- frames_number (int) – total number of frames
Raises: ApplicationError – on application error
Return type:
-
set_led_movie_full
(movie)[source]¶ Uploads movie
Parameters: movie – file-like object that points to movie file. Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_mode
(mode)[source]¶ Sets new LED operation mode.
Parameters: mode (str) – Mode to set. One of ‘movie’, ‘playlist’, ‘rt’, ‘demo’, ‘effect’ or ‘off’. Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_movies_current
(movie_id)[source]¶ Sets which movie in the movie list to play
See also
Parameters: movie_id (int) – id of movie to play Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_movies_full
(movie)[source]¶ Uploads a movie to the movie list
Presumes that ‘set_movies_new’ has been called earlier with the movie params.
See also
Parameters: movie – file-like object that points to movie file. Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_movies_new
(name, uid, dtype, nleds, nframes, fps)[source]¶ Prepares the upload of a new movie to the movie list by setting its parameters
See also
Parameters: - name (str) – name of new movie
- uid (str) – unique id of new movie
- dtype (str) – descriptor_type, one of rgb_raw, rgbw_raw, or aww_raw
- nleds (int) – number of leds
- nframes (int) – number of frames
- fps (int) – frames per second of the new movie
Raises: ApplicationError – on application error
Return type:
-
set_mqtt_config
(broker_host=None, broker_port=None, client_id=None, user=None, interval=None)[source]¶ Sets the mqtt configuration parameters
See also
Parameters: - broker_host (str) – optional broker host
- broker_port (int) – optional broker port
- client_id (str) – optional client_id
- user (str) – optional user name
- interval (int) – optional keep alive interval
Raises: ApplicationError – on application error
Return type:
-
set_network_mode_ap
(password=None)[source]¶ Sets network mode to Access Point
If password is given, changes the Access Point password (after which you have to connect again with the new password)
Parameters: password (str) – new password to set Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_network_mode_station
(ssid=None, password=None)[source]¶ Sets network mode to Station
The first time you need to provide an ssid and password for the WIFI to connect to.
Parameters: - ssid (str) – SSID of the access point to connect to
- password (str) – password to use
Raises: ApplicationError – on application error
Return type:
-
set_playlist
(entries)[source]¶ Sets a new playlist
See also
Parameters: entries (list) – list of playlist entries each with keys “unique_id” and “duration” Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_playlist_current
(movie_id)[source]¶ Sets which movie in the playlist to play
See also
Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_rt_frame_rest
(frame)[source]¶ Uploads a frame in rt-mode, using the ordinary restful protocol
Parameters: frame – file-like object that points to frame file. Raises: ApplicationError – on application error Return type: ApplicationResponse
-
set_rt_frame_socket
(frame, version, leds_number=None)[source]¶ Uploads a frame in rt-mode, over an UDP socket. This is much faster than the restful protocol.
Parameters: - frame – file-like object representing the frame
- version – use protocol version 1, 2 or 3
- leds_number (int) – the number of leds (only used in version 1)
Return type: None
-
set_saturation
(saturation=None, enabled=True, relative=False)[source]¶ Sets new saturation or enable/disable desaturation
Parameters: - saturation – new saturation in range of 0..100 or a relative change in -100..100 or None if no change is requested
- enabled (bool) – set to False if no desaturation should be applied
- relative (bool) – set to True to make a relative change
Raises: ApplicationError – on application error
Return type:
-
set_timer
(time_on, time_off, time_now=None)[source]¶ Sets new timer
Parameters: - time_on (int) – time when to turn lights on. In seconds after midnight. To disable use -1.
- time_off (int) – time when to turn lights off. In seconds after midnight. To disable use -1.
- time_now (int or None) – current time in seconds after midnight. Determined automatically if not set.
Raises: ApplicationError – on application error
Return type:
-
udpclient
¶ Client for sending UDP packets to the realtime port
Returns: the UDP client UDPClient()
.Return type: udp_client.UDPClient
-
-
class
xled.control.
HighControlInterface
(host, hw_address=None)[source]¶ Bases:
xled.control.ControlInterface
High level interface to control specific device
-
get_formatted_timer
()[source]¶ Gets current time and timer
Returns: namedtuple of formatted entries: current time, turn on time, turn off time. Return type: namedtuple
-
set_static_color
(red, green, blue)[source]¶ Sets static color for all leds
Parameters: - red – integer between 0-255 representing red color
- green – integer between 0-255 representing green color
- blue – integer between 0-255 representing blue color
-
update_firmware
(stage0, stage1)[source]¶ Uploads firmware and runs update
Parameters: - stage0 – file-like object pointing to stage0 of firmware. Must support seek().
- stage1 – file-like object pointing to stage1 of firmware. Must support seek().
Raises: - ApplicationError – on application error
- HighInterfaceError – on error during update
-
static
write_static_movie
(file_obj, size, red, green, blue)[source]¶ Writes movie of single color
Parameters: - file_obj – file-like object to write movie to.
- size (int) – numbers of triples (RGB) to write to.
- red – integer between 0-255 representing red color
- green – integer between 0-255 representing green color
- blue – integer between 0-255 representing blue color
-
-
xled.control.
REALTIME_UDP_PORT_NUMBER
= 7777¶ UDP port to send realtime frames to
-
xled.control.
TIME_FORMAT
= '%H:%M:%S'¶ Time format as defined by C standard
xled.discover module¶
xled.discover¶
This module contains interface for discovery devices on the network
-
class
xled.discover.
DiscoveryInterface
(destination_host=None, receive_timeout=None)[source]¶ Bases:
object
Main interface to discover devices on the network
Starts an UDP ping agent in a background thread automatically after initialisation.
-
class
xled.discover.
InterfaceAgent
(ctx, pipe, loop=None, destination_host=None, receive_timeout=None)[source]¶ Bases:
object
This structure holds the context for our agent
This way it can be passed around cleanly to methods that need it.
Parameters: - ctx –
zmq.Context
object. - pipe – Pipe back to the main thread of to pass messages.
- loop – (optional) loop to use.
-
control_message
(event)[source]¶ Respond to control message from main application thread
Currently unused.
Parameters: event – anything.
-
get_mac_address
(ip_address)[source]¶ Gets the MAC address of the device at ip_address.
Parameters: ip_address – The IP address or hostname to the device Returns: The MAC address, or None in case of failure
-
handle_beacon
(fd, event)[source]¶ Reads response from nodes
Creates
Peer
objects and tracks them in self.peers. Finally sends messages through pipe to main application thread.Parameters: - fd – not used
- event – not used
-
peers
= None¶ Hash of known peers, fast lookup
-
process_new_peer
(hw_address, device_id, ip_address)[source]¶ Adds new peer and sends out status message
This is called when we receive a message from HW address we don’t have in a list of peers. Adds peer info in a list of peers sends out message JOINED message.
Parameters: - hw_address (str) – HW address of a device from which we have received a beacon. Must not exist in list of peers.
- device_id (str) – device ID decoded from a beacon
- ip_address (str) – IP address decoded from a beacon
-
process_seen_peer
(hw_address, device_id, ip_address)[source]¶ Updates seen peer’s info and sends out status message
This is called when we receive a message from a peer that we track as seen peers. Updates expiry time for a peer and sends out ALIVE message. If device ID or IP address changed updates peer’s info and sends out message RENAMED or ADDRESS_CHANGED messages respectively.
Parameters: - hw_address (str) – HW address of a device from which we have received a beacon. Must exist in list of peers.
- device_id (str) – device ID decoded from a beacon
- ip_address (str) – IP address decoded from a beacon
-
reap_peers
()[source]¶ Removes peers whose activity wasn’t seen for a long time
Called periodically. Sends messages through pipe to main application thread.
- ctx –
-
xled.discover.
PEER_EXPIRY
= 5.0¶ After how many seconds the device is considered offline
-
xled.discover.
PING_INTERVAL
= 1.0¶ Interval in seconds
-
xled.discover.
PING_MESSAGE
= b'\x01discover'¶ Message to send in ping requests
-
xled.discover.
PING_PORT_NUMBER
= 5555¶ Default port number to send pings
-
class
xled.discover.
Peer
(hw_address, device_id, ip_address)[source]¶ Bases:
object
Each object of this class represents one device on the network
Parameters: - hw_address – Hardware (MAC) address of a device.
- device_id – Id of the device.
- ip_address – IP address of a device.
-
xled.discover.
discover
(find_id=None, destination_host=None, timeout=None)[source]¶ Wrapper of
xdiscover()
to return first entry
-
xled.discover.
pipe
(ctx)[source]¶ Create an inproc PAIR pipe
Used for communicating between parent and children.
Parameters: ctx – zmq.Context
object.Returns: parent socket, child socket. Return type: tuple
-
xled.discover.
xdiscover
(find_id=None, destination_host=None, timeout=None)[source]¶ Generator discover all devices or device of specific id
Device can be specified either by id or by host.
Parameters: - find_id (str) – (optional) Device id to look for. If not set first node that responded is returned.
- destination_host (str) – (optional) Ping selected node only.
- timeout (float) – (optional) Number of seconds until discovery timeouts.
Returns: namedtuple of hardware address, device id and host name.
Return type: namedtuple
Raises: DiscoverTimeout – timeout exceeded while waiting for a device
xled.exceptions module¶
-
exception
xled.exceptions.
ApplicationError
(*args, **kwargs)[source]¶ Bases:
xled.exceptions.XledException
Application didn’t return successful status code
-
exception
xled.exceptions.
AuthenticationError
(*args, **kwargs)[source]¶ Bases:
xled.exceptions.XledException
Authentication handshake wasn’t successful
-
exception
xled.exceptions.
DiscoverTimeout
(*args, **kwargs)[source]¶ Bases:
xled.exceptions.XledException
Signal that timeout occurred while discover is looking for a device
-
exception
xled.exceptions.
HighInterfaceError
(*args, **kwargs)[source]¶ Bases:
xled.exceptions.XledException
High level interface error
-
exception
xled.exceptions.
ReceiveTimeout
(*args, **kwargs)[source]¶ Bases:
xled.exceptions.XledException
Signal that timeout occurred while waiting for data
-
exception
xled.exceptions.
TokenExpiredError
(*args, **kwargs)[source]¶ Bases:
xled.exceptions.XledException
Token is no longer valid
-
exception
xled.exceptions.
ValidationError
(*args, **kwargs)[source]¶ Bases:
xled.exceptions.XledException
Validation of challenge response wasn’t successful
xled.response module¶
-
class
xled.response.
ApplicationResponse
(response=None)[source]¶ Bases:
collections.abc.Mapping
The
ApplicationResponse
object, which contains a server’s response to an HTTP request.Parameters: response ( requests.Response
or None) – to which this is a response. Can be later set as an attribute.-
data
¶ Response content as dict
-
ok
¶ Returns True if
status_code
is 1000, False if not.First this attribute checks if parent response is ok. Then it checks if application response can be determined and finally if
status_code
is 1000.
-
raise_for_status
(propagate=True)[source]¶ Raises
ApplicationError
, if one occurred.Parameters: propagate (bool) – check status of underlying requests.Response
as well.Raises: ApplicationError – if response cannot be parsed as JSON or application status code wasn’t success (1000). Return type: None
-
status_code
¶ Integer Code of responded application status, e.g. 1000 or 1001
-
-
xled.response.
build_response
(response)[source]¶ Creates ApplicationResponse object out of Requests response
Parameters: response ( requests.Response
or None) – to which this is a response. Can be later set as an attribute.Return type: ApplicationResponse
xled.security module¶
xled.security¶
This module contains cryptographic functions to encrypt data with shared secret so it can be transferred over unencrypted connection.
See also
- Protocol details
- for various operations.
-
xled.security.
BUFFER_SIZE
= 65536¶ Read buffer size for sha1sum
-
xled.security.
SHARED_KEY_CHALLANGE
= b'evenmoresecret!!'¶ Default key to encrypt challenge in login phase
-
xled.security.
SHARED_KEY_WIFI
= b'supersecretkey!!'¶ Default key to encrypt WiFi password
-
xled.security.
derive_key
(shared_key, mac_address)[source]¶ Derives secret key from shared key and MAC address
MAC address is repeated to length of key. Then bytes on corresponding positions are xor-ed. Finally a string is created.
Parameters: - shared_key (str) – secret key
- mac_address (str) – MAC address in any format that netaddr.EUI recognizes
Returns: derived key
Return type: bytes
-
xled.security.
encrypt_wifi_password
(password, mac_address, key=b'supersecretkey!!')[source]¶ Encrypts password
This can be used to send password for WiFi in encrypted form over unencrypted channel. Ideally only device that knows shared secret key and has defined MAC address should be able to decrypt the message.
Parameters: - password (str) – password to encrypt
- mac_address (str) – MAC address of the remote device in any format that netaddr.EUI recognizes
- key (str) – (optional) shared key that device has to know
Returns: Base 64 encoded string of ciphertext of input password
Return type: str
-
xled.security.
make_challenge_response
(challenge_message, mac_address, key=b'evenmoresecret!!')[source]¶ Create challenge response from challenge
Used in initial login phase of communication with device. Could be used to check that device shares same shared secret and implements same algorithm to show that it is genuine.
Parameters: - challenge_message (str) – random message originally sent as challenge with login request
- mac_address (str) – MAC address of the remote device in any format that netaddr.EUI recognizes
- key (str) – (optional) shared key that device has to know
Returns: hashed ciphertext that must be equal to challenge-response in response to login call
Return type: str
-
xled.security.
rc4
(message, key)[source]¶ Simple wrapper for RC4 cipher that encrypts message with key :param str message: input to encrypt :param str key: encryption key :return: ciphertext :rtype: str
-
xled.security.
sha1sum
(fileobj)[source]¶ Computes SHA1 from file-like object
It is up to caller to open file for reading and close it afterwards.
Parameters: fileobj – file-like object Returns: SHA1 digest as hexdecimal digits only Return type: str
-
xled.security.
xor_strings
(message, key)[source]¶ Apply XOR operation on every corresponding byte
If key is shorter than message repeats it from the beginning until whole message is processed. :param bytes message: input message to encrypt :param bytes key: encryption key :return: encrypted cypher :rtype: bytearray
xled.udp_client module¶
xled.udp_client¶
A Simple UDP class
-
class
xled.udp_client.
UDPClient
(port, destination_host=None, broadcast=False, receive_timeout=None)[source]¶ Bases:
object
Creates simple UDP client
Object can be used either to send to broadcast or unicast address.
Parameters: - port (int) – destination port to connect to and from which received packets will be read.
- destination_host (str or None) – unicast IP address to send packets to. If
broadcast parameter is set to True and this parameter is left to None
DEFAULT_BROADCAST
is used automatically. - broadcast (bool) – use broadcast for a socket
-
handle
¶ Socket handler for send/recv
-
recv
(bufsize)[source]¶ Blocks until message is received
Skips messages received from any address stored in
own_addresses
.Parameters: bufsize (int) – the maximum amount of data to be received at once Returns: received message, sender address Return type: tuple
xled.util module¶
Module contents¶
xled package¶
xled is a library to control Twinkly LED lights. Basic usage:
>>> import xled
>>> control = xled.HighControlInterface('192.168.4.1')
>>> control.set_mode('demo')
>>> control.turn_off()
The other API calls are supported - see xled.control. Full documentation is at <http://xled.readthedocs.io/>.
copyright: |
|
---|---|
license: | MIT, see LICENSE for more details. |
The Contributor Guide¶
Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions¶
Report Bugs¶
Report bugs at https://github.com/scrool/xled/issues. Fill all sections for issue template.
Fix Bugs¶
Look through the GitHub issues for bugs. Anything tagged with “bug” and “help wanted” is open to whoever wants to implement it.
Generally project should implement only features described on https://xled-docs.readthedocs.io/ . Make sure you update device features, behavior, endpoints and so on there as well.
Write Documentation¶
xled could always use more documentation, whether as part of the official xled docs, in docstrings, or even on the web in blog posts, articles, and such.
Submit Feedback¶
The best way to send feedback is to file an issue at https://github.com/scrool/xled/issues.
If you are proposing a feature:
- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that this is a volunteer-driven project, and that contributions are welcome :)
Get Started!¶
Ready to contribute? Here’s how to set up xled for local development.
Fork the xled repo on GitHub.
Clone your fork locally:
$ git clone git@github.com:your_name_here/xled.git
Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:
$ mkvirtualenv xled $ cd xled/ $ pip install -r requirements_dev.txt $ pre-commit install $ python setup.py develop
Create a branch for local development:
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
When you’re done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:
$ flake8 xled tests $ python setup.py test or py.test $ tox
To get flake8 and tox, just pip install them into your virtualenv.
Commit your changes and push your branch to GitHub:
$ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature
Submit a pull request through the GitHub website.
Pull Request Guidelines¶
Before you submit a pull request, check that it meets these guidelines:
- The pull request should include tests.
- If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.
- The pull request should work for Python 2.7, 3.6, 3.7, 3.8, 3.9 and 3.10. Check https://github.com/scrool/xled/actions and make sure that the tests pass for all supported Python versions.
Credits¶
Development Lead¶
- Pavol Babinčák <scroolik@gmail.com>
Contributors¶
- Paul Webster (@PaulWebster)
- Artem Ignatyev (@timon)
- Anders Holst (@Anders-Holst)
History¶
0.7.0 (2021-11-28)¶
- Major changes:
- Add realtime UDP protocol including unit tests
- Add several missing rest calls in ControlInterface
- Unit test all methods in ControlInterface
- Other bugfixes and improvements:
- Provide a short guide how to install packages from PyPI
- Provide python_requires in setup.py
- Add project URLs to metadata
- Corrected import of security, and removed some old comments
- Make encrypt_wifi_password work also with python3
- More flexible parameters to set_mqtt_config
- Enable options in set_network_mode_ap and _station
- Enable relative values in set_brightness
- Make firmware_update compatible with Generation II
- Fix error in python setup.py install (fix #82)
- Use generic Exception in discover module
- On Python 2.7 ignore VCR deprecation warning
- On Python 2.7 ignore cryptography deprecation warning
- Fix dependencies for python 2.7
- Don’t debug log reapped devices
- Time format as hardcoded value instead of locale specific
- Raise an exception with a error message firmware update failed
- Get MAC address from gestalt API call
- Always UTF-8 decode response from JOINED event in discovery
- Log instead of print in discovery interface and return on unknown event
- If hw_address wasn’t possible to resolve don’t use None as a peer
- Configure all loggers used by CLI with cli_log.basic_config()
- Make response assertions less strict
- Reformat setup py so tox tests pass
- Documentation updates:
- Update example in README to use reflect change in API
- Add Gitter badge
- Update of xled and xled-docs should be done hand in hand
- Remove Enhancement section from Contributing as there is no such thing
- Write down support for OS, devices, python and guide to CLI
- Rewrite README file
- Fix documentation for set_led_movie_config
- Changes in CI/CD:
- Run linters as GitHub action
- Use generic python3 in black pre-commit config
- Configure pytest to collect tests only from tests/
- Use GitHub action for PyPI publish
- Update URL for CI from Travis to GitHub actions
- One more place to update supported python versions
- Make Travis environment python again
- Remove non-deploy section from travis.yml
- Fix typo in travis.yml dep install
- Ignore Flake8 error on Sphinx configuration file
- Run pytest directly from Tox
- Add bug report template for GitHub issues and reference it
- Switch to token authentication for deployment to pypi through Travis
- Changes in dependencies and python versions:
- Add 3.10 to supported Python versions
- Update coverage from 4.4.2 to 5.5
- Update pip from 20.2.3 to 21.1
- Update travis.yaml: remove python 3.5 and add 3.8 and 3.9
- Add 3.9 to supported Python versions
- Drop Python 3.5 support
- Drop compatibility code for Python version 3.4
- Add Python 3.8 as a supported language
- Update pip from 19.0.3 to 20.2.3
- Update sphinx from 1.6.5 to 3.0.4
0.6.1 (2020-01-17)¶
- Make tests with tox pass again so release can be automatically deployed:
- Add Black reformatter to tox linter envs
- Tox config: new linters env to run Flake8
- Tox config update: Flake8 against tests/ and setup.py as well
- Make xled.compat pass Flake8 for F821 undefined names
- Refactor beacon processing of seen/new peer into separate methods
- Reformat test_control test with black
- Make tox install test-only requires
- Use conditional deployment to pypi with travis only from master
0.6.0 (2020-01-15)¶
- Drop support for python 3.4
- Explicitly specify Linux as only operating system
- Automatically refresh token if expired
- Add brightness management
- Check response is OK before trying to decode JSON from body
- Use id instead of name in discovery
- Device class representing the device
- Get network status in control interface
- Use response from alive device to check if we reached discover timeout
- Provide generator xdiscover() to return all or specific devices
- Support timeout for discovery
- When agent stops stop ping task and processing responses
- Provide close() for UDPClient and use it on DiscoveryInterface.stop()
- Do not continue receiving more data if UDP recv timeouts
- Other bugfixes and improvements:
- Fix assertions
- Expose HighControlInterface on package level
- If ApplicationError is raised, store value of response attribute
- Allow disable/enable of brightness without value change
- Update wheel from 0.30.0 to 0.33.1
- Update pip from 9.0.1 to 19.0.3
- Add python 3.6 and 3.7 to Travis config
0.5.0 (2018-12-09)¶
- CLI to update firmware
- Example of library call and CLI usage
- Option to select device by hostname in CLI and ping in discovery
- New HighControlInterface() to aggregate and abstract low-level calls
- CLI and HighControlInterface way to set static single color
- Other bugfixes and improvements:
- Fix typo in CLI error message
- Print message before discovery on CLI
- Refactor: join consecutive strings on same line
- Print better message after device has been discovered over CLI
- Regenerate documentation index of a package
- Fix typo in control.set_mode() documentation
- Return named tuple in discover.discover()
- Use discovery and named tuple in example of library use
- Do not assert return value in ControlInterface.set_led_movie_full()
- Return ApplicationResponse for ControlInterface.set_led_movie_config()
- Return ApplicationResponse for control.ControlInterface.led_reset()
- Remove unneeded debug message from DiscoveryInterface.__init__()
0.4.0 (2018-12-03)¶
- Support Python 3.6 and 3.7 including tests and documentation
- Python 3 support with pyzmq >= 17.0 and Tornado 5
- Remove redundant udplib
- Other Python 3 compatibility:
- In Python 3+ import Mapping from collections.abc
- Python 3 compatible encoding of discovered IP and HW address and name
- Make xled.security.xor_strings() compatible with Python 2 and 3
- Treat PING_MESSAGE as bytes to simplify handling Python 2 and 3
- Other bugfixes and improvements:
- Remove mention of PyPy from docs as it wasn’t ever tested on it
- Improve robustness with sending messages from agent to interface
- Escape display of binary challenge in debug log of xled.auth
- Ignore (usually own) PING_MESSAGE on network when handling responses
0.3.1 (2018-11-27)¶
- Update changelog for version 0.3.0
- Update description in setup.py to refer to CLI
- Fix JSON payload sent to server for firmware update.
0.3.0 (2018-11-27)¶
- CLI interface
- Discovery interface - currently works only on Python 2
- Add support for API led/movie/full and corresponding CLI upload-movie
- New Authentication mechanism - use session
- Rename authentication module from long challenge_response_auth to auth
- Change interface of ApplicationResponse to collections.Mapping
- Python files reformatted with Black
- Other bugfixes and improvements:
- Really show ApplicationResponse status in repr() when available
- Catch JSONDecodeError in Python 3.5+ in ApplicationResponse
- New shortcut method ok() of ApplicationResponse
- Make ApplicationResponse’s attribute status_code @property
- Improve error reporting during parsing of ApplicationResponse
- If repr() of ApplicationResponse is called parse response first
- Check status of underlying requests’ Response if requested
- Accept requests’ response as attribute to class ApplicationResponse
- Move generate_challenge to security module
- Unit tests for control interface
- Run unit tests on supported python versions with tox and Travis
- Configuration for pre-commit-hooks
- Initial pyup configuration
- Don’t run Tox on Travis on Python 3.3
- Update coverage
0.2.1 (2018-01-02)¶
- Add missing MANIFEST.in
- Configure Travis for automatic deployment to PyPI
0.2.0 (2018-01-02)¶
- First Python control interface.
0.1.0 (2017-12-17)¶
- Low level control interface.