├── MANIFEST.in ├── NooLite_F ├── MTRF64 │ ├── __init__.py │ ├── MTRF64Adapter.py │ └── MTRF64Controller.py ├── __init__.py ├── Sensors.py ├── Modules.py └── NooLiteFController.py ├── LICENSE.txt ├── setup.py ├── .gitignore └── README.rst /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the license file 2 | include LICENSE.txt 3 | 4 | # Include the data files 5 | recursive-include NooLite_F * 6 | recursive-exclude NooLite_F test.py 7 | -------------------------------------------------------------------------------- /NooLite_F/MTRF64/__init__.py: -------------------------------------------------------------------------------- 1 | from NooLite_F.MTRF64.MTRF64Adapter import MTRF64Adapter, IncomingData, OutgoingData, Command, Mode, Action, ResponseCode, IncomingDataException 2 | from NooLite_F.MTRF64.MTRF64Controller import MTRF64Controller 3 | 4 | -------------------------------------------------------------------------------- /NooLite_F/__init__.py: -------------------------------------------------------------------------------- 1 | from NooLite_F.NooLiteFController import NooLiteFController, Direction, NooLiteFListener, BatteryState, ModuleMode 2 | from NooLite_F.NooLiteFController import ModuleInfo, ModuleBaseStateInfo, ModuleExtraStateInfo, ModuleChannelsStateInfo, ModuleState, ServiceModeState, InputMode, DimmerCorrectionConfig, ModuleConfig, NooliteModeState 3 | from NooLite_F.NooLiteFController import ResponseBaseInfo, ResponseExtraInfo, ResponseChannelsInfo, ResponseModuleConfig, ResponseDimmerCorrectionConfig 4 | from NooLite_F.Modules import Switch, ExtendedSwitch, Dimmer, RGBLed 5 | from NooLite_F.Sensors import GenericListener, TempHumiSensor, MotionSensor, RemoteController, RGBRemoteController 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sergey Prytkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import io 3 | 4 | with io.open('README.rst', encoding="utf-8") as readme_file: 5 | long_description = readme_file.read() 6 | 7 | setup( 8 | name="NooLite_F", 9 | packages=["NooLite_F", "NooLite_F.MTRF64"], 10 | version="0.1.3", 11 | license="MIT License", 12 | description="Module to work with NooLite/NooLite-F modules via MTRF-64/MTRF-64-USB adapter", 13 | long_description=long_description, 14 | author="Sergey Prytkov", 15 | author_email="sergej.prytkov@gmail.com", 16 | url="https://github.com/SergejPr/NooLite-F", 17 | keywords="noolite noolite-f noolitef", 18 | install_requires=["pyserial"], 19 | platforms="any", 20 | classifiers=[ 21 | "Development Status :: 4 - Beta", 22 | "Intended Audience :: Developers", 23 | "Topic :: Software Development :: Libraries :: Python Modules", 24 | "Topic :: Home Automation", 25 | "Topic :: System :: Hardware", 26 | "License :: OSI Approved :: MIT License", 27 | "Programming Language :: Python :: 3.5", 28 | "Programming Language :: Python :: 3.6", 29 | "Programming Language :: Python :: 3.8", 30 | "Programming Language :: Python :: 3.9", 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | # idea 104 | .idea/ -------------------------------------------------------------------------------- /NooLite_F/Sensors.py: -------------------------------------------------------------------------------- 1 | from NooLite_F import NooLiteFController, NooLiteFListener, BatteryState, Direction 2 | 3 | 4 | class GenericListener(NooLiteFListener): 5 | 6 | def __init__(self, controller: NooLiteFController, channel: int, on_battery_low=None): 7 | self._controller = controller 8 | self._channel = channel 9 | self._battery_low_listener = on_battery_low 10 | self._controller.add_listener(channel, self) 11 | 12 | def release(self): 13 | self._controller.remove_listener(self._channel, self) 14 | self._controller = None 15 | 16 | def on_battery_low(self): 17 | if self._battery_low_listener is not None: 18 | self._battery_low_listener() 19 | 20 | 21 | class TempHumiSensor(GenericListener): 22 | 23 | def __init__(self, controller: NooLiteFController, channel: int, on_data): 24 | super().__init__(controller, channel) 25 | self._on_data_listener = on_data 26 | 27 | def on_temp_humi(self, temp: float, humi: int, battery: BatteryState, analog: float): 28 | if self._on_data_listener is not None: 29 | self._on_data_listener(temp, humi, analog, battery) 30 | 31 | 32 | class MotionSensor(GenericListener): 33 | 34 | def __init__(self, controller: NooLiteFController, channel: int, on_motion, on_battery_low=None): 35 | super().__init__(controller, channel, on_battery_low) 36 | self._motion_listener = on_motion 37 | 38 | def on_temporary_on(self, duration: int): 39 | if self._motion_listener is not None: 40 | self._motion_listener(duration) 41 | 42 | 43 | class BinarySensor(GenericListener): 44 | def __init__(self, controller: NooLiteFController, channel: int, on_on=None, on_off=None, on_battery_low=None): 45 | 46 | super().__init__(controller, channel, on_battery_low) 47 | self._on_listener = on_on 48 | self._off_listener = on_off 49 | 50 | def on_on(self): 51 | if self._on_listener is not None: 52 | self._on_listener() 53 | 54 | def on_off(self): 55 | if self._off_listener is not None: 56 | self._off_listener() 57 | 58 | 59 | class RemoteController(GenericListener): 60 | def __init__(self, controller: NooLiteFController, channel: int, on_on=None, on_off=None, on_switch=None, 61 | on_tune_start=None, on_tune_back=None, on_tune_stop=None, on_load_preset=None, on_save_preset=None, 62 | on_battery_low=None): 63 | 64 | super().__init__(controller, channel, on_battery_low) 65 | self._on_listener = on_on 66 | self._off_listener = on_off 67 | self._switch_listener = on_switch 68 | self._tune_listener = on_tune_start 69 | self._back_listener = on_tune_back 70 | self._stop_listener = on_tune_stop 71 | self._load_preset_listener = on_load_preset 72 | self._save_preset_listener = on_save_preset 73 | 74 | def on_on(self): 75 | if self._on_listener is not None: 76 | self._on_listener() 77 | 78 | def on_off(self): 79 | if self._off_listener is not None: 80 | self._off_listener() 81 | 82 | def on_switch(self): 83 | if self._switch_listener is not None: 84 | self._switch_listener() 85 | 86 | def on_load_preset(self): 87 | if self._load_preset_listener is not None: 88 | self._load_preset_listener() 89 | 90 | def on_save_preset(self): 91 | if self._save_preset_listener is not None: 92 | self._save_preset_listener() 93 | 94 | def on_brightness_tune(self, direction: Direction): 95 | if self._tune_listener is not None: 96 | self._tune_listener(direction) 97 | 98 | def on_brightness_tune_stop(self): 99 | if self._stop_listener is not None: 100 | self._stop_listener() 101 | 102 | def on_brightness_tune_back(self): 103 | if self._back_listener is not None: 104 | self._back_listener() 105 | 106 | 107 | class RGBRemoteController(GenericListener): 108 | def __init__(self, controller: NooLiteFController, channel: int, on_switch=None, on_tune_back=None, 109 | on_tune_stop=None, on_roll_color=None, on_switch_color=None, on_switch_mode=None, on_switch_speed=None, 110 | on_battery_low=None): 111 | 112 | super().__init__(controller, channel, on_battery_low) 113 | self._switch_listener = on_switch 114 | self._back_listener = on_tune_back 115 | self._stop_listener = on_tune_stop 116 | self._roll_color_listener = on_roll_color 117 | self._switch_color_listener = on_switch_color 118 | self._switch_mode_listener = on_switch_mode 119 | self._switch_speed_listener = on_switch_speed 120 | 121 | def on_switch(self): 122 | if self._switch_listener is not None: 123 | self._switch_listener() 124 | 125 | def on_brightness_tune_stop(self): 126 | if self._stop_listener is not None: 127 | self._stop_listener() 128 | 129 | def on_brightness_tune_back(self): 130 | if self._back_listener is not None: 131 | self._back_listener() 132 | 133 | def on_switch_rgb_color(self): 134 | if self._switch_color_listener is not None: 135 | self._switch_color_listener() 136 | 137 | def on_roll_rgb_color(self): 138 | if self._roll_color_listener is not None: 139 | self._roll_color_listener() 140 | 141 | def on_switch_rgb_mode(self): 142 | if self._switch_mode_listener is not None: 143 | self._switch_mode_listener() 144 | 145 | def on_switch_rgb_mode_speed(self): 146 | if self._switch_speed_listener is not None: 147 | self._switch_speed_listener() 148 | -------------------------------------------------------------------------------- /NooLite_F/MTRF64/MTRF64Adapter.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from enum import IntEnum 4 | from serial import Serial 5 | from struct import Struct 6 | from time import sleep 7 | 8 | from threading import * 9 | from queue import Queue, Empty 10 | 11 | 12 | class Command(IntEnum): 13 | OFF = 0, 14 | BRIGHT_DOWN = 1, 15 | ON = 2, 16 | BRIGHT_UP = 3, 17 | SWITCH = 4, 18 | BRIGHT_BACK = 5, 19 | SET_BRIGHTNESS = 6, 20 | LOAD_PRESET = 7, 21 | SAVE_PRESET = 8, 22 | UNBIND = 9, 23 | STOP_BRIGHT = 10, 24 | BRIGHT_STEP_DOWN = 11, 25 | BRIGHT_STEP_UP = 12, 26 | BRIGHT_REG = 13, 27 | BIND = 15, 28 | ROLL_COLOR = 16, 29 | SWITCH_COLOR = 17, 30 | SWITCH_MODE = 18, 31 | SPEED_MODE = 19, 32 | BATTERY_LOW = 20, 33 | SENS_TEMP_HUMI = 21, 34 | TEMPORARY_ON = 25, 35 | MODES = 26, 36 | READ_STATE = 128, 37 | WRITE_STATE = 129, 38 | SEND_STATE = 130, 39 | SERVICE = 131, 40 | CLEAR_MEMORY = 132 41 | 42 | 43 | class Mode(IntEnum): 44 | TX = 0, 45 | RX = 1, 46 | TX_F = 2, 47 | RX_F = 3, 48 | SERVICE = 4, 49 | FIRMWARE_UPDATE = 5 50 | 51 | 52 | class ResponseCode(IntEnum): 53 | SUCCESS = 0, 54 | NO_RESPONSE = 1, 55 | ERROR = 2, 56 | BIND_SUCCESS = 3 57 | 58 | 59 | class Action(IntEnum): 60 | SEND_COMMAND = 0, 61 | SEND_BROADCAST_COMMAND = 1, 62 | READ_RESPONSE = 2, 63 | BIND_MODE_ON = 3, 64 | BIND_MODE_OFF = 4, 65 | CLEAR_CHANNEL = 5, 66 | CLEAR_MEMORY = 6, 67 | UNBIND_ADDRESS_FROM_CHANNEL = 7, 68 | SEND_COMMAND_TO_ID_IN_CHANNEL = 8, 69 | SEND_COMMAND_TO_ID = 9 70 | 71 | 72 | class IncomingDataException(Exception): 73 | """Base class for response exceptions.""" 74 | 75 | 76 | class OutgoingData(object): 77 | mode = Mode.TX 78 | action = Action.SEND_COMMAND 79 | channel = 0 80 | command = Command.OFF 81 | format = 0 82 | data = bytearray(4) 83 | id = 0 84 | 85 | def __repr__(self): 86 | return "".format(id(self), self.mode, self.action, self.channel, self.command, self.format, self.data, self.id) 87 | 88 | 89 | class IncomingData(object): 90 | mode = None 91 | status = None 92 | channel = None 93 | command = None 94 | count = None 95 | format = None 96 | data = None 97 | id = None 98 | 99 | def __repr__(self): 100 | return "".format(id(self), self.mode, self.status, self.count, self.channel, self.command, self.format, self.data, self.id) 101 | 102 | 103 | _LOGGER = logging.getLogger("MTRF64USBAdapter") 104 | _LOGGER.setLevel(logging.WARNING) 105 | _LOGGER_HANDLER = logging.StreamHandler() 106 | _LOGGER_HANDLER.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S")) 107 | _LOGGER.addHandler(_LOGGER_HANDLER) 108 | 109 | DEFAULT_BAUDRATE = 9600 110 | 111 | 112 | class MTRF64Adapter(object): 113 | _packet_size = 17 114 | _serial = None 115 | _read_thread = None 116 | _command_response_queue = Queue() 117 | _incoming_queue = Queue() 118 | _send_lock = Lock() 119 | _listener_thread = None 120 | _listener = None 121 | _is_released = False 122 | 123 | def __init__(self, port: str, baudrate: int = DEFAULT_BAUDRATE, on_receive_data=None): 124 | self._serial = Serial(baudrate=baudrate) 125 | self._serial.port = port 126 | self._serial.open() 127 | 128 | self._listener = on_receive_data 129 | 130 | self._read_thread = Thread(target=self._read_loop) 131 | self._read_thread.daemon = True 132 | self._read_thread.start() 133 | 134 | self._listener_thread = Thread(target=self._read_from_incoming_queue) 135 | self._listener_thread.daemon = True 136 | self._listener_thread.start() 137 | 138 | def release(self): 139 | self._is_released = True 140 | self._serial.close() 141 | self._incoming_queue.put(None) 142 | self._listener = None 143 | 144 | def send(self, data: OutgoingData) -> [IncomingData]: 145 | responses = [] 146 | 147 | packet = self._build(data) 148 | with self._send_lock: 149 | _LOGGER.debug("Send:\n - request: {0},\n - packet: {1}".format(data, packet)) 150 | self._command_response_queue.queue.clear() 151 | self._serial.write(packet) 152 | 153 | try: 154 | while True: 155 | response = self._command_response_queue.get(timeout=2) 156 | responses.append(response) 157 | if response.count == 0: 158 | break 159 | 160 | except Empty as err: 161 | _LOGGER.error("Error receiving response: {0}.".format(err)) 162 | 163 | # For NooLite.TX we should make a bit delay. Adapter send the response without waiting until command was delivered. 164 | # So if we send new command until previous command was sent to module, adapter will ignore new command. Note: 165 | if data.mode == Mode.TX or data.mode == Mode.RX: 166 | sleep(0.2) 167 | 168 | return responses 169 | 170 | # Private 171 | def _crc(self, data) -> int: 172 | sum = 0 173 | for i in range(0, len(data)): 174 | sum = sum + data[i] 175 | sum = sum & 0xFF 176 | return sum 177 | 178 | def _build(self, data: OutgoingData) -> bytes: 179 | format_begin = Struct(">BBBBBBB4sI") 180 | format_end = Struct("BB") 181 | 182 | packet = format_begin.pack(171, data.mode, data.action, 0, data.channel, data.command, data.format, data.data, data.id) 183 | packet_end = format_end.pack(self._crc(packet), 172) 184 | 185 | packet = packet + packet_end 186 | 187 | return packet 188 | 189 | def _parse(self, packet: bytes) -> IncomingData: 190 | if len(packet) != self._packet_size: 191 | raise IncomingDataException("Invalid packet size: {0}".format(len(packet))) 192 | 193 | format = Struct(">BBBBBBB4sIBB") 194 | 195 | data = IncomingData() 196 | start_byte, data.mode, data.status, data.count, data.channel, data.command, data.format, data.data, data.id, crc, stop_byte = format.unpack(packet) 197 | 198 | if (start_byte != 173) or (stop_byte != 174) or (crc != self._crc(packet[0:-2])): 199 | raise IncomingDataException("Invalid response") 200 | 201 | return data 202 | 203 | def _read_loop(self): 204 | while True: 205 | packet = self._serial.read(self._packet_size) 206 | 207 | if self._is_released: 208 | break 209 | 210 | try: 211 | data = self._parse(packet) 212 | _LOGGER.debug("Receive:\n - packet: {0},\n - data: {1}".format(packet, data)) 213 | 214 | if data.mode == Mode.TX or data.mode == Mode.TX_F: 215 | self._command_response_queue.put(data) 216 | elif data.mode == Mode.RX or data.mode == Mode.RX_F: 217 | self._incoming_queue.put(data) 218 | else: 219 | pass 220 | 221 | except IncomingDataException as err: 222 | _LOGGER.error("Packet error: {0}".format(err)) 223 | pass 224 | 225 | def _read_from_incoming_queue(self): 226 | while True: 227 | input_data = self._incoming_queue.get() 228 | 229 | if self._is_released: 230 | break 231 | 232 | if self._listener is not None: 233 | self._listener(input_data) 234 | -------------------------------------------------------------------------------- /NooLite_F/Modules.py: -------------------------------------------------------------------------------- 1 | from NooLite_F import NooLiteFController, ModuleMode, Direction, ModuleConfig, DimmerCorrectionConfig 2 | from NooLite_F import ResponseBaseInfo, ResponseExtraInfo, ResponseChannelsInfo, ResponseModuleConfig, ResponseDimmerCorrectionConfig 3 | 4 | 5 | class Switch(object): 6 | 7 | def __init__(self, controller: NooLiteFController, module_id: int = None, channel: int = None, module_mode: ModuleMode = ModuleMode.NOOLITE_F, broadcast_mode: bool = False): 8 | self._channel = channel 9 | self._module_mode = module_mode 10 | self._broadcast_mode = broadcast_mode 11 | self._controller = controller 12 | self._module_id = module_id 13 | 14 | def on(self) -> [ResponseBaseInfo]: 15 | return self._controller.on(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 16 | 17 | def off(self) -> [ResponseBaseInfo]: 18 | return self._controller.off(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 19 | 20 | def switch(self) -> [ResponseBaseInfo]: 21 | return self._controller.switch(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 22 | 23 | def load_preset(self) -> [ResponseBaseInfo]: 24 | return self._controller.load_preset(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 25 | 26 | def save_preset(self) -> [ResponseBaseInfo]: 27 | return self._controller.save_preset(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 28 | 29 | def read_state(self) -> [ResponseBaseInfo]: 30 | return self._controller.read_state(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 31 | 32 | def read_extra_state(self) -> [ResponseExtraInfo]: 33 | return self._controller.read_extra_state(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 34 | 35 | def read_channels_state(self) -> [ResponseChannelsInfo]: 36 | return self._controller.read_channels_state(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 37 | 38 | def bind(self) -> [ResponseBaseInfo]: 39 | return self._controller.bind(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 40 | 41 | def unbind(self) -> [ResponseBaseInfo]: 42 | return self._controller.unbind(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 43 | 44 | def set_service_mode(self, state: bool) -> [ResponseBaseInfo]: 45 | return self._controller.set_service_mode(state, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 46 | 47 | def read_config(self) -> [ResponseModuleConfig]: 48 | return self._controller.read_module_config(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 49 | 50 | def write_config(self, config: ModuleConfig) -> [ResponseModuleConfig]: 51 | return self._controller.write_module_config(config, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 52 | 53 | 54 | class ExtendedSwitch(Switch): 55 | 56 | def temporary_on(self, duration: int) -> [ResponseBaseInfo]: 57 | return self._controller.temporary_on(duration, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 58 | 59 | def set_temporary_on_mode(self, enabled: bool) -> [ResponseBaseInfo]: 60 | return self._controller.set_temporary_on_mode(enabled, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 61 | 62 | 63 | class Dimmer(ExtendedSwitch): 64 | 65 | def brightness_tune(self, direction: Direction) -> [ResponseBaseInfo]: 66 | return self._controller.brightness_tune(direction, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 67 | 68 | def brightness_tune_back(self) -> [ResponseBaseInfo]: 69 | return self._controller.brightness_tune_back(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 70 | 71 | def brightness_tune_stop(self) -> [ResponseBaseInfo]: 72 | return self._controller.brightness_tune_stop(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 73 | 74 | def brightness_tune_custom(self, direction: Direction, speed: float) -> [ResponseBaseInfo]: 75 | return self._controller.brightness_tune_custom(direction, speed, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 76 | 77 | def brightness_tune_step(self, direction: Direction, step: int = None) -> [ResponseBaseInfo]: 78 | return self._controller.brightness_tune_step(direction, step, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 79 | 80 | def set_brightness(self, brightness: float) -> [ResponseBaseInfo]: 81 | return self._controller.set_brightness(brightness, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 82 | 83 | def read_dimmer_correction(self) -> [ResponseDimmerCorrectionConfig]: 84 | return self._controller.read_dimmer_correction(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 85 | 86 | def write_dimmer_correction(self, config: DimmerCorrectionConfig) -> [ResponseDimmerCorrectionConfig]: 87 | return self._controller.write_dimmer_correction(config, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 88 | 89 | 90 | class Fan(ExtendedSwitch): 91 | 92 | def speed_tune(self, direction: Direction) -> [ResponseBaseInfo]: 93 | return self._controller.brightness_tune(direction, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 94 | 95 | def speed_tune_back(self) -> [ResponseBaseInfo]: 96 | return self._controller.brightness_tune_back(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 97 | 98 | def speed_tune_stop(self) -> [ResponseBaseInfo]: 99 | return self._controller.brightness_tune_stop(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 100 | 101 | def speed_tune_custom(self, direction: Direction, speed: float) -> [ResponseBaseInfo]: 102 | return self._controller.brightness_tune_custom(direction, speed, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 103 | 104 | def speed_tune_step(self, direction: Direction, step: int = None) -> [ResponseBaseInfo]: 105 | return self._controller.brightness_tune_step(direction, step, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 106 | 107 | def set_speed(self, speed: float) -> [ResponseBaseInfo]: 108 | return self._controller.set_brightness(speed, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 109 | 110 | def read_dimmer_correction(self) -> [ResponseDimmerCorrectionConfig]: 111 | return self._controller.read_dimmer_correction(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 112 | 113 | def write_dimmer_correction(self, config: DimmerCorrectionConfig) -> [ResponseDimmerCorrectionConfig]: 114 | return self._controller.write_dimmer_correction(config, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 115 | 116 | 117 | class RGBLed(Switch): 118 | 119 | def brightness_tune(self, direction: Direction) -> [ResponseBaseInfo]: 120 | return self._controller.brightness_tune(direction, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 121 | 122 | def brightness_tune_back(self) -> [ResponseBaseInfo]: 123 | return self._controller.brightness_tune_back(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 124 | 125 | def brightness_tune_stop(self) -> [ResponseBaseInfo]: 126 | return self._controller.brightness_tune_stop(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 127 | 128 | def set_brightness(self, brightness: float) -> [ResponseBaseInfo]: 129 | return self._controller.set_brightness(brightness, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 130 | 131 | def roll_rgb_color(self) -> [ResponseBaseInfo]: 132 | return self._controller.roll_rgb_color(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 133 | 134 | def switch_rgb_color(self) -> [ResponseBaseInfo]: 135 | return self._controller.switch_rgb_color(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 136 | 137 | def switch_rgb_mode(self) -> [ResponseBaseInfo]: 138 | return self._controller.switch_rgb_mode(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 139 | 140 | def switch_rgb_mode_speed(self) -> [ResponseBaseInfo]: 141 | return self._controller.switch_rgb_mode_speed(self._module_id, self._channel, self._broadcast_mode, self._module_mode) 142 | 143 | def set_rgb_brightness(self, red: float, green: float, blue: float) -> [ResponseBaseInfo]: 144 | return self._controller.set_rgb_brightness(red, green, blue, self._module_id, self._channel, self._broadcast_mode, self._module_mode) 145 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | NooLite-F 2 | ========= 3 | 4 | Python module to work with NooLite-F (MTRF-64-USB, MTRF-64) 5 | You can find more details about MTRF-64/MTRF-64-USB api on official NooLite site: 6 | 7 | * https://noo.by/ 8 | * https://noo.by/images/instructions/Adaptery%20i%20moduli/rukovodstvo-po-ekspluatatsii-mtrf-64-usb-a.pdf 9 | * https://noo.by/images/instructions/Adaptery%20i%20moduli/mtrf-64-usb.pdf 10 | * https://noo.by/images/instructions/Adaptery%20i%20moduli/rukovodstvo-po-ekspluatatsii-mtrf-64-a.pdf 11 | * https://noo.by/images/instructions/Adaptery%20i%20moduli/mtrf-64.pdf 12 | 13 | **Supported Python versions:** Python 3.5 and later 14 | 15 | Send commands to modules 16 | ======================== 17 | 18 | There are possible three ways of sending commands to modules: 19 | 20 | 21 | Using adapter 22 | ------------- 23 | You can work directly with adapter:: 24 | 25 | adapter = MTRF64USBAdapter("COM3") 26 | 27 | request = Request() 28 | request.action = Action.SEND_COMMAND 29 | request.mode = Mode.TX 30 | request.channel = 60 31 | request.command = Command.TEMPORARY_ON 32 | request.format = 6 33 | request.data[0] = 1 34 | 35 | response = adapter.send(request) 36 | 37 | print(response) 38 | 39 | request = Request() 40 | request.action = Action.SEND_COMMAND_TO_ID 41 | request.mode = Mode.TX_F 42 | request.id = 0x5023 43 | request.command = Command.SWITCH 44 | 45 | response = adapter.send(request) 46 | 47 | print(response) 48 | 49 | 50 | **Note:** Request and response directly maps to low-level api for adapter. 51 | 52 | 53 | Using controller 54 | ---------------- 55 | 56 | You can use MTRF64Controller and abstract from manual request data creating. Just call appropriate function:: 57 | 58 | controller = MTRF64Controller("COM3") 59 | controller.set_brightness(channel=60, brightness=0.3, module_mode=ModuleMode.NOOLITE) 60 | 61 | controller.switch(module_id=0x5435, module_mode=ModuleMode.NOOLITE_F) 62 | 63 | 64 | Controller supports following commands: 65 | 66 | * on - turn on the module 67 | * off - turn off the module 68 | * switch - switch module state 69 | 70 | * temporary_on - turn on the module for a specified time 71 | * set_temporary_on_mode - enable/disable "temporary on" mode 72 | 73 | * bright_tune - start to increase/decrease brightness 74 | * bright_tune_back - invert direction of the brightness change 75 | * bright_tune_stop - stop brightness changing 76 | * bright_tune_custom - start to increase/decrease brightness with a specified speed 77 | * bright_step - increase/decrease brightness once with a specified step 78 | * set_brightness - set brightness 79 | 80 | * load_preset - load saved module state from preset 81 | * save_preset - save current module state as preset 82 | 83 | * roll_rgb_color - start color changing **(only for RGB Led modules)** 84 | * switch_rgb_color - switch color **(only for RGB Led modules)** 85 | * switch_rgb_mode - switch color changing modes **(only for RGB Led modules)** 86 | * switch_rgb_mode_speed - switch speed of the color changing **(only for RGB Led modules)** 87 | * set_rgb_brightness - set brightness for each rgb color **(only for RGB Led modules)** 88 | 89 | * read_state - read module state **(only for NooLite-F modules)** 90 | * read_extra_state - read additional module state **(only for NooLite-F modules)** 91 | * read_channels_state - read information about available channels for binding **(only for NooLite-F modules)** 92 | 93 | * read_module_config - read current module configuration **(only for NooLite-F modules)** 94 | * write_module_config - write new module configuration **(only for NooLite-F modules)** 95 | 96 | * read_dimmer_correction - read dimmer corrections values **(only for NooLite-F modules)** 97 | * write_dimmer_correction - write new dimmer corrections values **(only for NooLite-F modules)** 98 | 99 | * bind - send bind command to module 100 | * unbind - send unbind command to module 101 | * set_service_mode - turn on/off the service mode on module **(only for NooLite-F modules)** 102 | 103 | Each command can accept following parameters: 104 | 105 | - module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 106 | - channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 107 | - broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 108 | - module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 109 | 110 | Some commands require additional parameters. For more details see inline help. 111 | 112 | 113 | In response for each command returns: 114 | 115 | * for **nooLite-F** modules returns array which contains command result, module info and it state for each module that are binded with selected channel. 116 | * for **nooLite** modules returns nothing. 117 | 118 | Command result equals True if command send successfully, otherwise False. Module info contains information about module: module, id, type, firmware version. Module state contains information about module state: (on/off/temporary on), current brightness and bind mode (on/off):: 119 | 120 | [ 121 | [(True, , )], 122 | [(True, , )] 123 | ] 124 | 125 | Some state and config command can return extra info about module state/config. 126 | If command result is False, then module info and state are None.:: 127 | 128 | [(False, None, None)] 129 | 130 | 131 | Using module wrappers 132 | --------------------- 133 | You can use special classes that are wrappers around controller. Each class is representation of the 134 | concrete module or modules assigned with specific channel:: 135 | 136 | controller = MTRF64Controller("COM3") 137 | dimmer = Dimmer(controller, channel=62, module_mode=ModuleMode.NOOLITE) 138 | dimmer.set_brightness(0.4) 139 | 140 | switch = Switch(controller, channel=60, module_mode=ModuleMode.NOOLITE) 141 | switch.on() 142 | 143 | switch = Switch(controller, module_id=0x5023, module_mode=ModuleMode.NOOLITE_F) 144 | switch.switch() 145 | 146 | 147 | Available module wrappers: 148 | 149 | * **Switch** - supports on/off, toggle, preset. Also supports services methods for bind/unbind. 150 | * **ExtendedSwitch** - In additional to Switch, supports temporary on. 151 | * **Dimmer** - In additional to ExtendedSwitch supports brightness managing. 152 | * **RGBLed** - supports toggle, brightness management, rgb color management. 153 | * **Fan** - the same as **Dimmer**, uses for manage fans (thanks to mrukavishnikov ( https://github.com/mrukavishnikov )). 154 | 155 | Receiving commands from remote controls 156 | ======================================= 157 | 158 | You can also use several ways to receive data from remote controllers and sensors. 159 | 160 | 161 | Using adapter listener 162 | ---------------------- 163 | 164 | You can receive data from remote controllers using MTRF64USBAdapter directly. For it you should pass a listener method into adapter constructor. 165 | This method will be call each time when adapter get data from sensors or remote controls:: 166 | 167 | def on_receive_data(incoming_data: IncomingData): 168 | print("data: {0}".format(incoming_data)) 169 | 170 | adapter = MTRF64USBAdapter("COM3", on_receive_data) 171 | 172 | 173 | Using controller listener 174 | ------------------------- 175 | 176 | You can create special command listener and assign it with concrete channel in controller. The controller get incoming data, handle it and call appropriate method in listener. 177 | So you should not worry about it:: 178 | 179 | controller = MTRF64Controller("COM3") 180 | switch = Dimmer(controller, channel=62, module_mode=ModuleMode.NOOLITE) 181 | 182 | class MyRemoteController(RemoteControllerListener): 183 | 184 | def on_on(self): 185 | switch.on() 186 | 187 | def on_off(self): 188 | switch.off() 189 | 190 | def on_switch(self): 191 | switch.switch() 192 | 193 | def on_brightness_tune(self, direction: BrightnessDirection): 194 | switch.brightness_tune(direction) 195 | 196 | def on_brightness_tune_stop(self): 197 | switch.brightness_tune_stop() 198 | 199 | def on_brightness_tune_back(self): 200 | switch.brightness_tune_back() 201 | 202 | 203 | class MySensor(RemoteControllerListener): 204 | def on_temp_humi(self, temp: float, humi: int, battery: BatteryState, analog: float): 205 | print("temp: {0}, humidity: {1}".format(temp, humi)) 206 | 207 | 208 | remoteController = MyRemoteController() 209 | sensor = MySensor() 210 | 211 | controller.add_listener(1, remoteController) 212 | controller.add_listener(2, sensor) 213 | 214 | while True: 215 | sleep(60) 216 | 217 | 218 | Using sensor wrappers 219 | --------------------- 220 | 221 | And in the end you can use a special wrappers around Controller and RemoteControllerListener. Just create it, set channel and appropriate listeners:: 222 | 223 | def on_temp(temp, humi, battery, analog): 224 | print("temp: {0}, humi: {1}, battery_state: {2}, analog: {3}".format(temp, humi, battery, analog)) 225 | 226 | def on_battery(): 227 | print("battery") 228 | 229 | def on_switch(): 230 | print("switch") 231 | 232 | def on_tune_back(): 233 | print("tune back") 234 | 235 | def on_tune_stop(): 236 | print("tune stop") 237 | 238 | def on_roll_color(): 239 | print("roll color") 240 | 241 | def on_switch_color(): 242 | print("switch color") 243 | 244 | def on_switch_mode(): 245 | print("switch mode") 246 | 247 | def on_switch_speed(): 248 | print("switch speed") 249 | 250 | 251 | controller = MTRF64Controller("COM3") 252 | 253 | tempSensor = TempHumiSensor(controller, 9, on_temp, on_battery) 254 | rgb = RGBRemoteController(controller, 63, on_switch, on_tune_back, on_tune_stop, on_roll_color, on_switch_color, on_switch_mode, on_switch_speed, on_battery) 255 | 256 | while True: 257 | sleep(60) 258 | 259 | 260 | Available wrappers: 261 | 262 | * **TempHumiSensor** - supports receiving data from temperature and humidity sensors. 263 | * **MotionSensor** - supports receiving data from motion sensor. 264 | * **BinarySensor** - supports receiving data from wet and opening sensor. 265 | * **RemoteController** - supports receiving commands from standard NooLite remote controllers. 266 | * **RGBRemoteController** - supports receiving commands from RGB Remote controller. 267 | 268 | 269 | Note 270 | ==== 271 | 272 | Tested with MTRF-64-USB adapter and modules: 273 | 274 | * SLF-1-300 (NooLite-F, switch module) 275 | * SRF-1-3000 (NooLite-F, smart power socket) 276 | * SD-1-180 (NooLite, RGB Module) 277 | * SU-1-500 (NooLite, switch module) 278 | * SUF-1-300 (NooLite-F, switch module) 279 | * PM112 (NooLite, motion sensor) 280 | * PT111 (NooLite, temperature and humidity sensor) 281 | * PB211 (NooLite, remote controller) 282 | * PK315 (Noolite, remote controller) 283 | * PU112-2 (NooLite, RGB remote controller) 284 | 285 | 286 | Breaking changes: 287 | ================= 288 | 289 | v0.1.0 290 | ------ 291 | 292 | * change parameters order in TempHumi sensor callback from *(temp, humi, battery, analog)* to *(temp, humi, analog, battery)* -------------------------------------------------------------------------------- /NooLite_F/MTRF64/MTRF64Controller.py: -------------------------------------------------------------------------------- 1 | from NooLite_F import NooLiteFController, Direction, ModuleMode, NooLiteFListener, BatteryState 2 | from NooLite_F import ModuleInfo, ModuleBaseStateInfo, ModuleExtraStateInfo, ModuleChannelsStateInfo, ModuleState, ServiceModeState, DimmerCorrectionConfig, ModuleConfig 3 | from NooLite_F import NooliteModeState, InputMode 4 | from NooLite_F import ResponseBaseInfo, ResponseExtraInfo, ResponseChannelsInfo, ResponseModuleConfig, ResponseDimmerCorrectionConfig 5 | from NooLite_F.MTRF64 import IncomingData, Command, Mode, Action, OutgoingData, ResponseCode, MTRF64Adapter 6 | from NooLite_F.MTRF64.MTRF64Adapter import DEFAULT_BAUDRATE 7 | 8 | from abc import ABC, abstractmethod 9 | from typing import TypeVar, Generic, List, Tuple 10 | 11 | 12 | T = TypeVar('T') 13 | V = TypeVar('V') 14 | 15 | 16 | class OutgoingDataException(Exception): 17 | """Base class for response exceptions.""" 18 | pass 19 | 20 | 21 | class Parser(ABC, Generic[T, V]): 22 | @abstractmethod 23 | def parse(self, data: T) -> V: 24 | pass 25 | 26 | 27 | class ModuleInfoParser(Parser[IncomingData, ModuleInfo]): 28 | def parse(self, data: IncomingData) -> ModuleInfo: 29 | info = None 30 | if data.command == Command.SEND_STATE and (data.format in (0, 1, 2)): 31 | info = ModuleInfo() 32 | info.type = data.data[0] 33 | info.firmware = data.data[1] 34 | info.id = data.id 35 | 36 | return info 37 | 38 | 39 | class ModuleBaseStateInfoParser(Parser[IncomingData, ModuleBaseStateInfo]): 40 | def parse(self, data: IncomingData) -> ModuleBaseStateInfo: 41 | info = None 42 | if data.command == Command.SEND_STATE and data.format == 0: 43 | info = ModuleBaseStateInfo() 44 | 45 | states = { 46 | 0: ModuleState.OFF, 47 | 1: ModuleState.ON, 48 | 2: ModuleState.TEMPORARY_ON, 49 | } 50 | 51 | state = data.data[2] 52 | 53 | if state in states: 54 | info.state = states[state] 55 | 56 | if (data.data[2] & 0x80) == 0x80: 57 | info.service_mode = ServiceModeState.BIND_ON 58 | else: 59 | info.service_mode = ServiceModeState.BIND_OFF 60 | 61 | info.brightness = data.data[3] / 255 62 | 63 | return info 64 | 65 | 66 | class ModuleExtraStateInfoParser(Parser[IncomingData, ModuleExtraStateInfo]): 67 | def parse(self, data: IncomingData) -> ModuleExtraStateInfo: 68 | info = None 69 | if data.command == Command.SEND_STATE and data.format == 1: 70 | info = ModuleExtraStateInfo() 71 | info.extra_input_state = (data.data[2] > 0) 72 | 73 | if (data.data[3] & 0x02) == 0x02: 74 | info.noolite_mode_state = NooliteModeState.DISABLED 75 | elif (data.data[3] & 0x01) == 0x01: 76 | info.noolite_mode_state = NooliteModeState.TEMPORARY_DISABLED 77 | else: 78 | info.noolite_mode_state = NooliteModeState.ENABLED 79 | 80 | return info 81 | 82 | 83 | class ModuleChannelsInfoParser(Parser[IncomingData, ModuleChannelsStateInfo]): 84 | def parse(self, data: IncomingData) -> ModuleChannelsStateInfo: 85 | info = None 86 | if data.command == Command.SEND_STATE and data.format == 2: 87 | info = ModuleChannelsStateInfo() 88 | info.noolite_cells = data.data[2] 89 | info.noolite_f_cells = data.data[3] 90 | 91 | return info 92 | 93 | 94 | class ModuleConfigurationParser(Parser[IncomingData, ModuleConfig]): 95 | 96 | def state(self, data: int, bit_num: int) -> bool: 97 | return (data & 1 << bit_num) == (1 << bit_num) 98 | 99 | def parse(self, data: IncomingData) -> ModuleConfig: 100 | info = None 101 | if data.command == Command.SEND_STATE and data.format == 16: 102 | info = ModuleConfig() 103 | 104 | info.save_state_mode = self.state(data.data[0], 0) 105 | info.dimmer_mode = self.state(data.data[0], 1) 106 | info.noolite_support = self.state(data.data[0], 2) 107 | info.init_state = self.state(data.data[0], 5) 108 | info.noolite_retranslation = self.state(data.data[0], 6) 109 | 110 | extra_input_modes = { 111 | 0: InputMode.SWITCH, 112 | 1: InputMode.BUTTON, 113 | 2: InputMode.BREAKER, 114 | 3: InputMode.DISABLED, 115 | } 116 | 117 | extra_input_mode = (data.data[0] & 0x18) >> 3 118 | if extra_input_mode in extra_input_modes: 119 | info.input_mode = extra_input_modes[extra_input_mode] 120 | 121 | return info 122 | 123 | 124 | class BrightnessConfigurationParser(Parser[IncomingData, ModuleConfig]): 125 | def parse(self, data: IncomingData) -> DimmerCorrectionConfig: 126 | info = None 127 | if data.command == Command.SEND_STATE and data.format == 17: 128 | info = DimmerCorrectionConfig() 129 | info.max_level = data.data[0] / 255 130 | info.min_level = data.data[1] / 255 131 | return info 132 | 133 | 134 | class MTRF64Controller(NooLiteFController): 135 | 136 | _adapter = None 137 | _listener_map = {} 138 | 139 | _mode_map = { 140 | ModuleMode.NOOLITE: Mode.TX, 141 | ModuleMode.NOOLITE_F: Mode.TX_F, 142 | } 143 | 144 | def __init__(self, port: str, baudrate: int = DEFAULT_BAUDRATE): 145 | self._adapter = MTRF64Adapter(port, baudrate, self._on_receive) 146 | 147 | def release(self): 148 | self._adapter.release() 149 | self._adapter = None 150 | self._listener_map = {} 151 | 152 | # Private 153 | def _command_mode(self, module_mode: ModuleMode) -> Mode: 154 | return self._mode_map[module_mode] 155 | 156 | def _send_module_command(self, module_id, channel: int, command: Command, broadcast, mode: Mode, command_data: bytearray = None, fmt: int = None) -> List[IncomingData]: 157 | data = OutgoingData() 158 | 159 | data.mode = mode 160 | data.command = command 161 | 162 | if module_id is not None: 163 | data.id = module_id 164 | if channel is None: 165 | data.action = Action.SEND_COMMAND_TO_ID 166 | else: 167 | data.channel = channel 168 | data.action = Action.SEND_COMMAND_TO_ID_IN_CHANNEL 169 | elif channel is not None: 170 | data.channel = channel 171 | if broadcast and mode == Mode.TX_F: # Broadcast used only for NOOLITE-F mode 172 | data.action = Action.SEND_BROADCAST_COMMAND 173 | else: 174 | data.action = Action.SEND_COMMAND 175 | else: 176 | raise OutgoingDataException( 177 | "Module_id and channel are not specified. You must specify at least one of them.") 178 | 179 | if command_data is not None: 180 | data.data = command_data 181 | 182 | if fmt is not None: 183 | data.format = fmt 184 | 185 | return self._adapter.send(data) 186 | 187 | def _send_module_base_command(self, module_id, channel: int, command: Command, broadcast, mode: Mode, command_data: bytearray = None, fmt: int = None, parser: Parser[IncomingData, V] = ModuleBaseStateInfoParser()) -> List[Tuple[bool, ModuleInfo, V]]: 188 | response = self._send_module_command(module_id, channel, command, broadcast, mode, command_data, fmt) 189 | return self._handle_base_command_responses(response, parser) 190 | 191 | def _send_module_config_command(self, module_id, channel: int, command: Command, broadcast, mode: Mode, command_data: bytearray = None, fmt: int = None, parser: Parser[IncomingData, V] = ModuleConfigurationParser()) -> List[Tuple[bool, V]]: 192 | response = self._send_module_command(module_id, channel, command, broadcast, mode, command_data, fmt) 193 | return self._handle_config_command_responses(response, parser) 194 | 195 | @staticmethod 196 | def _handle_base_command_responses(responses: List[IncomingData], parser: Parser[IncomingData, V]) -> List[Tuple[bool, ModuleInfo, V]]: 197 | results = [] 198 | for response in responses: 199 | info = ModuleInfoParser().parse(response) 200 | extra_info = parser.parse(response) 201 | status = response.status == ResponseCode.SUCCESS or response.status == ResponseCode.BIND_SUCCESS 202 | results.append((status, info, extra_info)) 203 | return results 204 | 205 | @staticmethod 206 | def _handle_config_command_responses(responses: List[IncomingData], parser: Parser[IncomingData, V]) -> List[Tuple[bool, V]]: 207 | results = [] 208 | for response in responses: 209 | config = parser.parse(response) 210 | status = response.status == ResponseCode.SUCCESS or response.status == ResponseCode.BIND_SUCCESS 211 | results.append((status, config)) 212 | return results 213 | 214 | @staticmethod 215 | def _convert_brightness(bright: float) -> int: 216 | if bright >= 1: 217 | value = 255 218 | elif bright <= 0: 219 | value = 0 220 | else: 221 | value = int((255 * bright) + 0.5) 222 | return value 223 | 224 | # Commands 225 | def off(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 226 | return self._send_module_base_command(module_id, channel, Command.OFF, broadcast, self._command_mode(module_mode)) 227 | 228 | def on(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 229 | return self._send_module_base_command(module_id, channel, Command.ON, broadcast, self._command_mode(module_mode)) 230 | 231 | def temporary_on(self, duration: int, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 232 | data = bytearray(2) 233 | data[0] = duration & 0x00FF 234 | data[1] = duration & 0xFF00 235 | return self._send_module_base_command(module_id, channel, Command.TEMPORARY_ON, broadcast, self._command_mode(module_mode), data, 6) 236 | 237 | def set_temporary_on_mode(self, enabled: bool, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 238 | data = bytearray(1) 239 | if not enabled: 240 | data[0] = 1 241 | return self._send_module_base_command(module_id, channel, Command.MODES, broadcast, self._command_mode(module_mode), data, 1) 242 | 243 | def switch(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 244 | return self._send_module_base_command(module_id, channel, Command.SWITCH, broadcast, self._command_mode(module_mode)) 245 | 246 | def brightness_tune(self, direction: Direction, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 247 | if direction == Direction.UP: 248 | command = Command.BRIGHT_UP 249 | else: 250 | command = Command.BRIGHT_DOWN 251 | return self._send_module_base_command(module_id, channel, command, broadcast, self._command_mode(module_mode)) 252 | 253 | def brightness_tune_back(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 254 | return self._send_module_base_command(module_id, channel, Command.BRIGHT_BACK, broadcast, self._command_mode(module_mode)) 255 | 256 | def brightness_tune_stop(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 257 | return self._send_module_base_command(module_id, channel, Command.STOP_BRIGHT, broadcast, self._command_mode(module_mode)) 258 | 259 | def brightness_tune_custom(self, direction: Direction, speed: float, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 260 | if speed >= 1: 261 | value = 127 262 | elif speed <= 0: 263 | value = 0 264 | else: 265 | value = int((speed * 127) + 0.5) 266 | 267 | if direction == Direction.DOWN: 268 | value = -value - 1 269 | 270 | data = bytearray(1) 271 | data[0] = value & 0xFF 272 | 273 | return self._send_module_base_command(module_id, channel, Command.BRIGHT_REG, broadcast, self._command_mode(module_mode), data, 1) 274 | 275 | def brightness_tune_step(self, direction: Direction, step: int = None, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 276 | data = None 277 | fmt = None 278 | 279 | if step is not None: 280 | fmt = 1 281 | data = bytearray(1) 282 | data[0] = step 283 | 284 | if direction == Direction.UP: 285 | command = Command.BRIGHT_STEP_UP 286 | else: 287 | command = Command.BRIGHT_STEP_DOWN 288 | 289 | return self._send_module_base_command(module_id, channel, command, broadcast, self._command_mode(module_mode), data, fmt) 290 | 291 | def set_brightness(self, brightness: float, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 292 | if brightness >= 1: 293 | value = 155 294 | elif brightness <= 0: 295 | value = 0 296 | else: 297 | value = 35 + int((120 * brightness) + 0.5) 298 | 299 | data = bytearray(1) 300 | data[0] = value 301 | 302 | return self._send_module_base_command(module_id, channel, Command.SET_BRIGHTNESS, broadcast, self._command_mode(module_mode), data, 1) 303 | 304 | def roll_rgb_color(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 305 | return self._send_module_base_command(module_id, channel, Command.ROLL_COLOR, broadcast, self._command_mode(module_mode)) 306 | 307 | def switch_rgb_color(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 308 | return self._send_module_base_command(module_id, channel, Command.SWITCH_COLOR, broadcast, self._command_mode(module_mode)) 309 | 310 | def switch_rgb_mode(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 311 | return self._send_module_base_command(module_id, channel, Command.SWITCH_MODE, broadcast, self._command_mode(module_mode)) 312 | 313 | def switch_rgb_mode_speed(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 314 | return self._send_module_base_command(module_id, channel, Command.SPEED_MODE, broadcast, self._command_mode(module_mode)) 315 | 316 | def set_rgb_brightness(self, red: float, green: float, blue: float, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 317 | data = bytearray(3) 318 | data[0] = self._convert_brightness(red) 319 | data[1] = self._convert_brightness(green) 320 | data[2] = self._convert_brightness(blue) 321 | return self._send_module_base_command(module_id, channel, Command.SET_BRIGHTNESS, broadcast, self._command_mode(module_mode), data, 3) 322 | 323 | def load_preset(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 324 | return self._send_module_base_command(module_id, channel, Command.LOAD_PRESET, broadcast, self._command_mode(module_mode)) 325 | 326 | def save_preset(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 327 | return self._send_module_base_command(module_id, channel, Command.SAVE_PRESET, broadcast, self._command_mode(module_mode)) 328 | 329 | def read_state(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 330 | return self._send_module_base_command(module_id, channel, Command.READ_STATE, broadcast, self._command_mode(module_mode)) 331 | 332 | def read_extra_state(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseExtraInfo]: 333 | return self._send_module_base_command(module_id, channel, Command.READ_STATE, broadcast, self._command_mode(module_mode), fmt=1, parser=ModuleExtraStateInfoParser()) 334 | 335 | def read_channels_state(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseChannelsInfo]: 336 | return self._send_module_base_command(module_id, channel, Command.READ_STATE, broadcast, self._command_mode(module_mode), fmt=2, parser=ModuleChannelsInfoParser()) 337 | 338 | def read_module_config(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseModuleConfig]: 339 | return self._send_module_config_command(module_id, channel, Command.READ_STATE, broadcast, self._command_mode(module_mode), fmt=16, parser=ModuleConfigurationParser()) 340 | 341 | def write_module_config(self, config: ModuleConfig, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseModuleConfig]: 342 | data = bytearray(4) 343 | 344 | save_state_mode = config.save_state_mode 345 | if save_state_mode is not None: 346 | data[2] = data[2] | 0x01 347 | if save_state_mode: 348 | data[0] = data[0] | 0x01 349 | 350 | dimmer_mode = config.dimmer_mode 351 | if dimmer_mode is not None: 352 | data[2] = data[2] | 0x02 353 | if dimmer_mode: 354 | data[0] = data[0] | 0x02 355 | 356 | noolite_support = config.noolite_support 357 | if noolite_support is not None: 358 | data[2] = data[2] | 0x04 359 | if noolite_support: 360 | data[0] = data[0] | 0x04 361 | 362 | input_mode = config.input_mode 363 | if input_mode is not None: 364 | data[2] = data[2] | 0x18 365 | if input_mode == InputMode.SWITCH: 366 | pass 367 | elif input_mode == InputMode.BUTTON: 368 | data[0] = data[0] | 0x08 369 | elif input_mode == InputMode.BREAKER: 370 | data[0] = data[0] | 0x10 371 | elif input_mode == InputMode.DISABLED: 372 | data[0] = data[0] | 0x18 373 | 374 | init_state = config.init_state 375 | if init_state is not None: 376 | data[2] = data[2] | 0x20 377 | if init_state: 378 | data[0] = data[0] | 0x20 379 | 380 | noolite_retranslation = config.noolite_retranslation 381 | if noolite_retranslation is not None: 382 | data[2] = data[2] | 0x40 383 | if noolite_retranslation: 384 | data[0] = data[0] | 0x40 385 | 386 | return self._send_module_config_command(module_id, channel, Command.WRITE_STATE, broadcast, self._command_mode(module_mode), data, fmt=16, parser=ModuleConfigurationParser()) 387 | 388 | def read_dimmer_correction(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseDimmerCorrectionConfig]: 389 | return self._send_module_config_command(module_id, channel, Command.READ_STATE, broadcast, self._command_mode(module_mode), fmt=17, parser=BrightnessConfigurationParser()) 390 | 391 | def write_dimmer_correction(self, config: DimmerCorrectionConfig, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseDimmerCorrectionConfig]: 392 | data = bytearray(4) 393 | 394 | data[0] = self._convert_brightness(config.max_level) 395 | data[1] = self._convert_brightness(config.min_level) 396 | data[2] = 0xFF 397 | data[3] = 0xFF 398 | 399 | return self._send_module_config_command(module_id, channel, Command.WRITE_STATE, broadcast, self._command_mode(module_mode), data, fmt=17, parser=BrightnessConfigurationParser()) 400 | 401 | def bind(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 402 | return self._send_module_base_command(module_id, channel, Command.BIND, broadcast, self._command_mode(module_mode)) 403 | 404 | def unbind(self, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 405 | return self._send_module_base_command(module_id, channel, Command.UNBIND, broadcast, self._command_mode(module_mode)) 406 | 407 | def set_service_mode(self, state: bool, module_id: int = None, channel: int = None, broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 408 | data = bytearray(1) 409 | if state: 410 | data[0] = 1 411 | return self._send_module_base_command(module_id, channel, Command.SERVICE, broadcast, self._command_mode(module_mode), data) 412 | 413 | def add_listener(self, channel: int, listener: NooLiteFListener): 414 | listeners = self._listener_map.get(channel, []) 415 | listeners.append(listener) 416 | self._listener_map[channel] = listeners 417 | 418 | def remove_listener(self, channel: int, listener: NooLiteFListener): 419 | listeners = self._listener_map.get(channel, []) 420 | listeners.remove(listener) 421 | if len(listeners) == 0: 422 | listeners = None 423 | self._listener_map[channel] = listeners 424 | 425 | # Listeners 426 | def _on_receive(self, incoming_data: IncomingData): 427 | listeners = self._listener_map.get(incoming_data.channel, None) 428 | 429 | if listeners is None: 430 | return 431 | 432 | for listener in listeners: 433 | if listener is None: 434 | return 435 | 436 | if incoming_data.command == Command.ON: 437 | listener.on_on() 438 | elif incoming_data.command == Command.OFF: 439 | listener.on_off() 440 | elif incoming_data.command == Command.SWITCH: 441 | listener.on_switch() 442 | elif incoming_data.command == Command.TEMPORARY_ON: 443 | delay = None 444 | if incoming_data.format == 5: 445 | delay = incoming_data.data[0] 446 | elif incoming_data.format == 6: 447 | delay = incoming_data.data[0] + (incoming_data.data[1] << 15) 448 | listener.on_temporary_on(delay) 449 | elif incoming_data.command == Command.BRIGHT_UP: 450 | listener.on_brightness_tune(Direction.UP) 451 | elif incoming_data.command == Command.BRIGHT_DOWN: 452 | listener.on_brightness_tune(Direction.DOWN) 453 | elif incoming_data.command == Command.BRIGHT_BACK: 454 | listener.on_brightness_tune_back() 455 | elif incoming_data.command == Command.BRIGHT_STEP_UP: 456 | if incoming_data.format == 1: 457 | step = incoming_data.data[0] 458 | else: 459 | step = None 460 | listener.on_brightness_tune_step(Direction.UP, step) 461 | elif incoming_data.command == Command.BRIGHT_STEP_DOWN: 462 | if incoming_data.format == 1: 463 | step = incoming_data.data[0] 464 | else: 465 | step = None 466 | listener.on_brightness_tune_step(Direction.DOWN, step) 467 | elif incoming_data.command == Command.STOP_BRIGHT: 468 | listener.on_brightness_tune_stop() 469 | elif incoming_data.command == Command.SET_BRIGHTNESS: 470 | if incoming_data.format == 3: 471 | red = incoming_data.data[0] / 255 472 | green = incoming_data.data[1] / 255 473 | blue = incoming_data.data[2] / 255 474 | listener.on_set_rgb_brightness(red, green, blue) 475 | elif incoming_data.format == 1: 476 | level = (incoming_data.data[0] - 35) / 120 477 | if level < 0: 478 | level = 0 479 | elif level > 1: 480 | level = 1 481 | listener.on_set_brightness(level) 482 | elif incoming_data.command == Command.LOAD_PRESET: 483 | listener.on_load_preset() 484 | elif incoming_data.command == Command.SAVE_PRESET: 485 | listener.on_save_preset() 486 | elif incoming_data.command == Command.ROLL_COLOR: 487 | listener.on_roll_rgb_color() 488 | elif incoming_data.command == Command.SWITCH_COLOR: 489 | listener.on_switch_rgb_color() 490 | elif incoming_data.command == Command.SWITCH_MODE: 491 | listener.on_switch_rgb_mode() 492 | elif incoming_data.command == Command.SPEED_MODE: 493 | listener.on_switch_rgb_mode_speed() 494 | elif incoming_data.command == Command.BRIGHT_REG: 495 | if incoming_data.format == 1: 496 | if incoming_data.data[0] & 0x80 == 0x80: 497 | direction = Direction.UP 498 | else: 499 | direction = Direction.DOWN 500 | speed = (incoming_data.data[0] & 0x7F) / 127 501 | listener.on_brightness_tune_custom(direction, speed) 502 | elif incoming_data.command == Command.SENS_TEMP_HUMI: 503 | # really from PT111 I get fmt = 7, but in specs is specify that fmt should be 3 504 | 505 | if incoming_data.format == 7: 506 | 507 | battery_bit = (incoming_data.data[1] & 0x80) >> 7 508 | if battery_bit: 509 | battery = BatteryState.LOW 510 | else: 511 | battery = BatteryState.OK 512 | 513 | temp_low = incoming_data.data[0] 514 | temp_hi = incoming_data.data[1] & 0x0F 515 | temp = (temp_hi << 8) + temp_low 516 | if temp > 0x0800: 517 | temp = -(0x1000 - temp) 518 | temp = temp / 10 519 | 520 | device_type = (incoming_data.data[1] & 0x70) >> 4 521 | if device_type == 2: 522 | humi = incoming_data.data[2] 523 | else: 524 | humi = None 525 | 526 | analog = incoming_data.data[3] / 255 527 | 528 | listener.on_temp_humi(temp, humi, battery, analog) 529 | elif incoming_data.command == Command.BATTERY_LOW: 530 | listener.on_battery_low() 531 | -------------------------------------------------------------------------------- /NooLite_F/NooLiteFController.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from enum import Enum 3 | from typing import Tuple, List 4 | 5 | 6 | class ModuleMode(Enum): 7 | NOOLITE = 0 8 | NOOLITE_F = 1 9 | 10 | 11 | class ModuleState(Enum): 12 | OFF = 1 13 | ON = 2 14 | TEMPORARY_ON = 3 15 | 16 | 17 | class NooliteModeState(Enum): 18 | DISABLED = 0 19 | TEMPORARY_DISABLED = 2 20 | ENABLED = 3 21 | 22 | 23 | class ServiceModeState(Enum): 24 | BIND_OFF = 0 25 | BIND_ON = 1 26 | 27 | 28 | class Direction(Enum): 29 | UP = 0 30 | DOWN = 1 31 | 32 | 33 | class BatteryState(Enum): 34 | OK = 0 35 | LOW = 1 36 | 37 | 38 | class InputMode(Enum): 39 | DISABLED = 0 40 | SWITCH = 1 41 | BUTTON = 2 42 | BREAKER = 3 43 | 44 | 45 | class ModuleConfig(object): 46 | dimmer_mode = None 47 | input_mode = None 48 | save_state_mode = None 49 | init_state = None 50 | noolite_support = None 51 | noolite_retranslation = None 52 | 53 | def __repr__(self): 54 | return "" \ 55 | .format(id(self), self.save_state_mode, self.dimmer_mode, self.noolite_support, self.input_mode, 56 | self.init_state, self.noolite_retranslation) 57 | 58 | 59 | class DimmerCorrectionConfig(object): 60 | min_level = 0.0 61 | max_level = 1.0 62 | 63 | def __repr__(self): 64 | return "" \ 65 | .format(id(self), self.min_level, self.max_level) 66 | 67 | 68 | class ModuleInfo(object): 69 | id = None 70 | firmware = None 71 | type = None 72 | 73 | def __repr__(self): 74 | return "" \ 75 | .format(id(self), self.id, self.type, self.firmware) 76 | 77 | 78 | class ModuleBaseStateInfo(object): 79 | state = None 80 | service_mode = None 81 | brightness = None 82 | 83 | def __repr__(self): 84 | return "" \ 85 | .format(id(self), self.state, self.brightness, self.service_mode) 86 | 87 | 88 | class ModuleExtraStateInfo(object): 89 | extra_input_state = None 90 | noolite_mode_state = None 91 | 92 | def __repr__(self): 93 | return "" \ 94 | .format(id(self), self.extra_input_state, self.noolite_mode_state) 95 | 96 | 97 | class ModuleChannelsStateInfo(object): 98 | noolite_cells = None 99 | noolite_f_cells = None 100 | 101 | def __repr__(self): 102 | return "" \ 103 | .format(id(self), self.noolite_cells, self.noolite_f_cells) 104 | 105 | 106 | ResponseBaseInfo = Tuple[bool, ModuleInfo, ModuleBaseStateInfo] 107 | ResponseExtraInfo = Tuple[bool, ModuleInfo, ModuleExtraStateInfo] 108 | ResponseChannelsInfo = Tuple[bool, ModuleInfo, ModuleChannelsStateInfo] 109 | ResponseModuleConfig = Tuple[bool, ModuleConfig] 110 | ResponseDimmerCorrectionConfig = Tuple[bool, DimmerCorrectionConfig] 111 | 112 | 113 | class NooLiteFListener(ABC): 114 | 115 | def on_on(self): 116 | pass 117 | 118 | def on_off(self): 119 | pass 120 | 121 | def on_switch(self): 122 | pass 123 | 124 | def on_load_preset(self): 125 | pass 126 | 127 | def on_save_preset(self): 128 | pass 129 | 130 | def on_temporary_on(self, duration: int): 131 | pass 132 | 133 | def on_brightness_tune(self, direction: Direction): 134 | pass 135 | 136 | def on_brightness_tune_back(self): 137 | pass 138 | 139 | def on_brightness_tune_stop(self): 140 | pass 141 | 142 | def on_brightness_tune_custom(self, direction: Direction, speed: float): 143 | pass 144 | 145 | def on_brightness_tune_step(self, direction: Direction, step: int = None): 146 | pass 147 | 148 | def on_set_brightness(self, brightness: float): 149 | pass 150 | 151 | def on_roll_rgb_color(self): 152 | pass 153 | 154 | def on_switch_rgb_color(self): 155 | pass 156 | 157 | def on_switch_rgb_mode(self): 158 | pass 159 | 160 | def on_switch_rgb_mode_speed(self): 161 | pass 162 | 163 | def on_set_rgb_brightness(self, red: float, green: float, blue: float): 164 | pass 165 | 166 | def on_temp_humi(self, temp: float, humi: int, battery: BatteryState, analog: float): 167 | pass 168 | 169 | def on_battery_low(self): 170 | pass 171 | 172 | 173 | class NooLiteFController(ABC): 174 | 175 | # Base power control 176 | @abstractmethod 177 | def off(self, module_id: int = None, channel: int = None, broadcast: bool = False, 178 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 179 | """ Turn off the modules 180 | 181 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 182 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 183 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 184 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 185 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 186 | """ 187 | pass 188 | 189 | @abstractmethod 190 | def on(self, module_id: int = None, channel: int = None, broadcast: bool = False, 191 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 192 | """ Turn on the modules 193 | 194 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 195 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 196 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 197 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 198 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 199 | """ 200 | pass 201 | 202 | @abstractmethod 203 | def temporary_on(self, duration: int, module_id: int = None, channel: int = None, broadcast: bool = False, 204 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 205 | """ Turn on the modules for a specified time interval 206 | 207 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 208 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 209 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 210 | :param duration: the time during which the modules will be turned on, duration measurement equals 5 sec. 211 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 212 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 213 | """ 214 | pass 215 | 216 | @abstractmethod 217 | def set_temporary_on_mode(self, enabled: bool, module_id: int = None, channel: int = None, broadcast: bool = False, 218 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 219 | """ Enable/disable "temporary on" mode 220 | 221 | :param enabled: new "temporary on" mode state (enable/disable). 222 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 223 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 224 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 225 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 226 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 227 | """ 228 | pass 229 | 230 | @abstractmethod 231 | def switch(self, module_id: int = None, channel: int = None, broadcast: bool = False, 232 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 233 | """ Switch modules mode (on/off) 234 | 235 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 236 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 237 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 238 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 239 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 240 | """ 241 | pass 242 | 243 | @abstractmethod 244 | def brightness_tune(self, direction: Direction, module_id: int = None, channel: int = None, 245 | broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ 246 | ResponseBaseInfo]: 247 | """ Start to increase/decrease brightness 248 | 249 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 250 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 251 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 252 | :param direction: direction of the brightness changing 253 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 254 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 255 | """ 256 | pass 257 | 258 | @abstractmethod 259 | def brightness_tune_back(self, module_id: int = None, channel: int = None, broadcast: bool = False, 260 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 261 | """ Invert direction of the brightness change 262 | 263 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 264 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 265 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 266 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 267 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 268 | """ 269 | pass 270 | 271 | @abstractmethod 272 | def brightness_tune_stop(self, module_id: int = None, channel: int = None, broadcast: bool = False, 273 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 274 | """ Stop brightness changing 275 | 276 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 277 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 278 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 279 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 280 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 281 | """ 282 | pass 283 | 284 | @abstractmethod 285 | def brightness_tune_custom(self, direction: Direction, speed: float, module_id: int = None, 286 | channel: int = None, broadcast: bool = False, 287 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 288 | """ Start to increase/decrease brightness with a specified speed 289 | 290 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 291 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 292 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 293 | :param direction: direction of the brightness changing 294 | :param speed: speed of the brightness changing. The range of value is 0 .. 1.0 295 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 296 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 297 | """ 298 | pass 299 | 300 | @abstractmethod 301 | def brightness_tune_step(self, direction: Direction, step: int = None, module_id: int = None, 302 | channel: int = None, broadcast: bool = False, 303 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 304 | """ Increase/decrease brightness once with a specified step 305 | 306 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 307 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 308 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 309 | :param direction: direction of the brightness changing 310 | :param step: step in microseconds. If specify then can have values in range (1..255) or 0 (it is means 256), by default step equals 64 311 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 312 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 313 | """ 314 | pass 315 | 316 | @abstractmethod 317 | def set_brightness(self, brightness: float, module_id: int = None, channel: int = None, broadcast: bool = False, 318 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 319 | """ Set brightness 320 | 321 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 322 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 323 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 324 | :param brightness: brightness level. The range of value is 0 .. 1.0 325 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 326 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 327 | """ 328 | pass 329 | 330 | @abstractmethod 331 | def roll_rgb_color(self, module_id: int = None, channel: int = None, broadcast: bool = False, 332 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 333 | """ Start color changing (only for RGB Led modules) 334 | 335 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 336 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 337 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 338 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 339 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 340 | """ 341 | pass 342 | 343 | @abstractmethod 344 | def switch_rgb_color(self, module_id: int = None, channel: int = None, broadcast: bool = False, 345 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 346 | """ Switch color (only for RGB Led modules) 347 | 348 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 349 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 350 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 351 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 352 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 353 | """ 354 | pass 355 | 356 | @abstractmethod 357 | def switch_rgb_mode(self, module_id: int = None, channel: int = None, broadcast: bool = False, 358 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 359 | """ Switch color changing modes (only for RGB Led modules) 360 | 361 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 362 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 363 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 364 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 365 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 366 | """ 367 | pass 368 | 369 | @abstractmethod 370 | def switch_rgb_mode_speed(self, module_id: int = None, channel: int = None, broadcast: bool = False, 371 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 372 | """ Switch speed of the color changing (only for RGB Led modules) 373 | 374 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 375 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 376 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 377 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 378 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 379 | """ 380 | pass 381 | 382 | @abstractmethod 383 | def set_rgb_brightness(self, red: float, green: float, blue: float, module_id: int = None, channel: int = None, 384 | broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ 385 | ResponseBaseInfo]: 386 | """ Set brightness for each rgb color (only for RGB Led modules) 387 | 388 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 389 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 390 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 391 | :param red: red color brightness level. The range of value is 0 .. 1.0 392 | :param green: green color brightness level. The range of value is 0 .. 1.0 393 | :param blue: blue color brightness level. The range of value is 0 .. 1.0 394 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 395 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 396 | """ 397 | pass 398 | 399 | @abstractmethod 400 | def load_preset(self, module_id: int = None, channel: int = None, broadcast: bool = False, 401 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 402 | """ Load saved module state from preset 403 | 404 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 405 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 406 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 407 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 408 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 409 | """ 410 | pass 411 | 412 | @abstractmethod 413 | def save_preset(self, module_id: int = None, channel: int = None, broadcast: bool = False, 414 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 415 | """ Save current module state as preset 416 | 417 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 418 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 419 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 420 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 421 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 422 | """ 423 | pass 424 | 425 | @abstractmethod 426 | def read_state(self, module_id: int = None, channel: int = None, broadcast: bool = False, 427 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 428 | """ Read module base state (only for NooLite-F modules) 429 | 430 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 431 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 432 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 433 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 434 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 435 | """ 436 | pass 437 | 438 | @abstractmethod 439 | def read_extra_state(self, module_id: int = None, channel: int = None, broadcast: bool = False, 440 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseExtraInfo]: 441 | """ Read module extra state: extra input state, noolite mode state (only for NooLite-F modules) 442 | 443 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 444 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 445 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 446 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 447 | :return: for nooLite-F command returns array which contains command result and module extra info for each module that are binded with selected channel. For nooLite modules returns nothing. 448 | """ 449 | pass 450 | 451 | @abstractmethod 452 | def read_channels_state(self, module_id: int = None, channel: int = None, broadcast: bool = False, 453 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseChannelsInfo]: 454 | """ Read module available cells count for binding (only for NooLite-F modules) 455 | 456 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 457 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 458 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 459 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 460 | :return: for nooLite-F command returns array which contains command result and module cells count for each module that are binded with selected channel. For nooLite modules returns nothing. 461 | """ 462 | pass 463 | 464 | @abstractmethod 465 | def read_module_config(self, module_id: int = None, channel: int = None, broadcast: bool = False, 466 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseModuleConfig]: 467 | """ Read module configuration (only for NooLite-F modules) 468 | 469 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 470 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 471 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 472 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 473 | :return: for nooLite-F command returns array which contains command result and module configuration for each module that are binded with selected channel. For nooLite modules returns nothing. 474 | """ 475 | pass 476 | 477 | @abstractmethod 478 | def write_module_config(self, config: ModuleConfig, module_id: int = None, channel: int = None, 479 | broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ 480 | ResponseModuleConfig]: 481 | """ Write module configuration (only for NooLite-F modules) 482 | 483 | :param config: the module configuration. If parameter in configuration has value None, then it won't changed. 484 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 485 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 486 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 487 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 488 | :return: for nooLite-F command returns array which contains command result and new module configuration for each module that are binded with selected channel. For nooLite modules returns nothing. 489 | """ 490 | pass 491 | 492 | @abstractmethod 493 | def read_dimmer_correction(self, module_id: int = None, channel: int = None, broadcast: bool = False, 494 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseDimmerCorrectionConfig]: 495 | """ Read dimmer correction values. Affects only on power modules in dimmer mode (only for NooLite-F modules) 496 | 497 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 498 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 499 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 500 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 501 | :return: for nooLite-F command returns array which contains command result and dimmer configuration for each module that are binded with selected channel. For nooLite modules returns nothing. 502 | """ 503 | pass 504 | 505 | @abstractmethod 506 | def write_dimmer_correction(self, config: DimmerCorrectionConfig, module_id: int = None, channel: int = None, 507 | broadcast: bool = False, module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ 508 | ResponseDimmerCorrectionConfig]: 509 | """ Writes dimmer correction values. Affects only on power modules in dimmer mode (only for NooLite-F modules) 510 | 511 | :param config: new dimmer correction for dimmer mode. 512 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 513 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 514 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 515 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 516 | :return: for nooLite-F command returns array which contains command result and new brightness configuration for each module that are binded with selected channel. For nooLite modules returns nothing. 517 | """ 518 | pass 519 | 520 | @abstractmethod 521 | def bind(self, module_id: int = None, channel: int = None, broadcast: bool = False, 522 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 523 | """ Send bind command to module 524 | 525 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 526 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 527 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 528 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 529 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 530 | """ 531 | pass 532 | 533 | @abstractmethod 534 | def unbind(self, module_id: int = None, channel: int = None, broadcast: bool = False, 535 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 536 | """ Send unbind command to module 537 | 538 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 539 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 540 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 541 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 542 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 543 | """ 544 | pass 545 | 546 | @abstractmethod 547 | def set_service_mode(self, state: bool, module_id: int = None, channel: int = None, broadcast: bool = False, 548 | module_mode: ModuleMode = ModuleMode.NOOLITE_F) -> List[ResponseBaseInfo]: 549 | """ Turn on/off the service mode on module (only for NooLite-F modules) 550 | 551 | :param state: new service mode state (on/off). 552 | :param module_id: the module id. The command will be send to module with specified id (used only for NOOLITE-F modules). 553 | :param channel: the number of the channel. The command will be send to all modules that are binded with selected channel. If module_id is also specified then command will be send only to appropriate device in channel. 554 | :param broadcast: broadcast mode. If True then command will be send simultaneously to all modules that are binded with selected channel (default - False). If module_id is specified or mode is NOOLITE then broadcast parameter will be ignored. 555 | :param module_mode: module work mode, used to determine adapter mode for send command (default - NOOLITE_F). 556 | :return: for nooLite-F command returns array which contains command result and module info for each module that are binded with selected channel. For nooLite modules returns nothing. 557 | """ 558 | pass 559 | 560 | @abstractmethod 561 | def add_listener(self, channel: int, listener: NooLiteFListener): 562 | """ Add the remote controls listener to channel. 563 | 564 | :param channel: channel to which the listener will be assigned 565 | :param listener: listener 566 | """ 567 | pass 568 | 569 | @abstractmethod 570 | def remove_listener(self, channel: int, listener: NooLiteFListener): 571 | """ Remove the remote controls listener from channel. 572 | 573 | :param channel: channel to which the listener will be assigned 574 | :param listener: listener 575 | """ 576 | pass 577 | --------------------------------------------------------------------------------