Skip to content

Commit a932ed0

Browse files
committed
2.0.0rc2
add M150 and M355 gcode support for Tapo lights
1 parent 657369c commit a932ed0

File tree

3 files changed

+90
-6
lines changed

3 files changed

+90
-6
lines changed

octoprint_tplinksmartplug/__init__.py

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import flask
1515
import octoprint.plugin
1616
from flask_babel import gettext
17-
from kasa import Device, Discover, Credentials
17+
from kasa import Device, Discover, Credentials, Module
1818
from octoprint.access.permissions import Permissions, ADMIN_GROUP
1919
from octoprint.events import Events
2020
from octoprint.util import RepeatedTimer
@@ -279,7 +279,7 @@ def on_settings_save(self, data):
279279
self.poll_status.start()
280280

281281
def get_settings_version(self):
282-
return 17
282+
return 18
283283

284284
def on_settings_migrate(self, target, current=None):
285285
if current is None or current < 5:
@@ -387,6 +387,13 @@ def on_settings_migrate(self, target, current=None):
387387
self._settings.set(["device_configs"], device_configs)
388388
self._settings.set(["arrSmartplugs"], arr_smartplugs_new)
389389

390+
if current is not None and current < 18:
391+
arr_smartplugs_new = []
392+
for plug in self._settings.get(['arrSmartplugs']):
393+
plug["receives_led_commands"] = False
394+
arr_smartplugs_new.append(plug)
395+
self._settings.set(["arrSmartplugs"], arr_smartplugs_new)
396+
390397
# ~~ AssetPlugin mixin
391398

392399
def get_assets(self):
@@ -974,6 +981,27 @@ def _shutdown_system(self):
974981

975982
# ~~ Utilities
976983

984+
def rgb2hsv(self, r, g, b):
985+
r, g, b = r/255.0, g/255.0, b/255.0
986+
return_val = dict()
987+
mx = max(r, g, b)
988+
mn = min(r, g, b)
989+
df = mx-mn
990+
if mx == mn:
991+
return_val["hue"] = 0
992+
elif mx == r:
993+
return_val["hue"] = (60 * ((g-b)/df) + 360) % 360
994+
elif mx == g:
995+
return_val["hue"] = (60 * ((b-r)/df) + 120) % 360
996+
elif mx == b:
997+
return_val["hue"] = (60 * ((r-g)/df) + 240) % 360
998+
if mx == 0:
999+
return_val["saturation"] = 0
1000+
else:
1001+
return_val["saturation"] = df/mx * 100
1002+
return_val["value"] = mx
1003+
return return_val
1004+
9771005
def get_device_config(self, plugip: str):
9781006
device_configs = self._settings.get(["device_configs"])
9791007
config_dict = device_configs.get(plugip, None)
@@ -1009,6 +1037,28 @@ async def connect_device(self, config_dict):
10091037
self._tplinksmartplug_logger.error(f"Error connecting to device: {e}")
10101038
return None
10111039

1040+
async def set_device_led(self, device, led_values, set_color=False) -> Optional[Device]:
1041+
try:
1042+
self._tplinksmartplug_logger.debug(f"Setting led from values: {led_values}")
1043+
light = device.modules[Module.Light]
1044+
if light:
1045+
if not device.is_on and led_values["LEDOn"]: # turn light on if off
1046+
await device.turn_on()
1047+
elif device.is_on and not led_values["LEDOn"]:
1048+
await device.turn_off()
1049+
await device.update()
1050+
return device
1051+
if "hsv" in device.features and set_color:
1052+
hsv_values = self.rgb2hsv(led_values["LEDRed"], led_values["LEDGreen"], led_values["LEDBlue"])
1053+
self._tplinksmartplug_logger.debug(f"Setting hsv values: {hsv_values}")
1054+
await light.set_hsv(int(hsv_values["hue"]), int(hsv_values["saturation"]), int(hsv_values["value"]))
1055+
if light.brightness != int(led_values["LEDBrightness"]):
1056+
await light.set_brightness(int(led_values["LEDBrightness"]))
1057+
await device.update()
1058+
except Exception as e:
1059+
self._tplinksmartplug_logger.error(f"Error connecting to device: {e}")
1060+
return device
1061+
10121062
def get_device(self, plugip: str) -> Optional[Device]:
10131063
try:
10141064
config_dict = self.get_device_config(plugip)
@@ -1077,7 +1127,7 @@ def process_gcode(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwa
10771127
self._waitForHeaters = False
10781128
self._reset_idle_timer()
10791129

1080-
if gcode not in ["M80", "M81"]:
1130+
if gcode not in ["M80", "M81", "M150", "M355"]:
10811131
return
10821132

10831133
if gcode == "M80":
@@ -1090,7 +1140,7 @@ def process_gcode(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwa
10901140
t.daemon = True
10911141
t.start()
10921142
return
1093-
if gcode == "M81":
1143+
elif gcode == "M81":
10941144
plugip = re.sub(r'^M81\s?', '', cmd)
10951145
self._tplinksmartplug_logger.debug(f"Received M81 command, attempting power off of {plugip}.")
10961146
plug = self.plug_search(self._settings.get(["arrSmartplugs"]), "ip", plugip)
@@ -1100,6 +1150,40 @@ def process_gcode(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwa
11001150
t.daemon = True
11011151
t.start()
11021152
return
1153+
elif gcode in ["M150", "M355"] and any(map(lambda plug_check: plug_check["receives_led_commands"] is True, self._settings.get(["arrSmartplugs"]))):
1154+
for led_device in self._settings.get(["arrSmartplugs"]):
1155+
if led_device["receives_led_commands"]:
1156+
device = self.get_device(led_device["ip"])
1157+
if device:
1158+
led_values = {'LEDRed': 255, 'LEDBlue': 255, 'LEDGreen': 255, 'LEDWhite': 255, 'LEDBrightness': 100, 'LEDOn': True}
1159+
cmd_split = cmd.upper().split()
1160+
for i in cmd_split:
1161+
first_char = str(i[0].upper())
1162+
led_data = str(i[1:].strip())
1163+
if not led_data.isdigit() and first_char != 'I':
1164+
self._tplinksmartplug_logger.debug(led_data)
1165+
return
1166+
1167+
if first_char == 'M':
1168+
continue
1169+
elif first_char == 'R':
1170+
led_values['LEDRed'] = int(led_data)
1171+
elif first_char == 'B':
1172+
led_values['LEDBlue'] = int(led_data)
1173+
elif first_char == 'G' or first_char == 'U':
1174+
led_values['LEDGreen'] = int(led_data)
1175+
elif first_char == "W":
1176+
led_values['LEDWhite'] = int(led_data)
1177+
elif first_char == "P":
1178+
led_values['LEDBrightness'] = int(float(led_data) / 255 * 100)
1179+
elif first_char == "S":
1180+
led_values['LEDOn'] = bool(int(led_data))
1181+
else:
1182+
self._tplinksmartplug_logger.debug(led_data)
1183+
1184+
self._tplinksmartplug_logger.debug(f"M150 command, attempting color change of {led_device['ip']}.")
1185+
self.worker.run_coroutine_threadsafe(self.set_device_led(device, led_values, set_color=(gcode == "M150")))
1186+
return
11031187

11041188
def process_at_command(self, comm_instance, phase, command, parameters, tags=None, *args, **kwargs):
11051189
if command == "TPLINKON":

octoprint_tplinksmartplug/templates/tplinksmartplug_settings.jinja2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@
227227
<tr>
228228
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('Automatically connect to printer after powering on this plug and waiting for configured delay.') }}" data-toggle="tooltip" data-bind="checked: autoConnect, tooltip: {container: '#TPLinkPlugEditor'}"/> {{ _('Auto Connect') }}</label><div class="input-append" data-toggle="tooltip" data-bind="tooltip: {container: '#TPLinkPlugEditor'}" title="{{ _('Amount of time to wait before attempting to connect to printer.') }}"><input type="number" data-bind="value: autoConnectDelay,enable: autoConnect" class="input input-mini" disabled /><span class="add-on">{{ _('secs') }}</span></div></div></td>
229229
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('Automatically disconnect from printer before waiting configured delay and powering off this plug.') }}" data-toggle="tooltip" data-bind="checked: autoDisconnect, tooltip: {container: '#TPLinkPlugEditor'}"/> {{ _('Auto Disconnect') }}</label><div class="input-append" data-toggle="tooltip" data-bind="tooltip: {container: '#TPLinkPlugEditor'}" title="{{ _('Amount of time to wait after disconnecting.') }}"><input type="number" data-bind="value: autoDisconnectDelay,enable: autoDisconnect" class="input input-mini" disabled /><span class="add-on">{{ _('secs') }}</span></div></div></td>
230-
<td></td>
230+
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('When enabled M150 commands will adjust this device\'s settings accordingly.') }}" data-toggle="tooltip" data-bind="checked: receives_led_commands, tooltip: {container: '#TPLinkPlugEditor'}" /> {{ _('LED Commands') }}</label></div></td>
231231
</tr>
232232
<tr>
233233
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('When enabled special GCODE commands can be monitored to power this plug on and off.') }}" data-toggle="tooltip" data-bind="checked: gcodeEnabled, tooltip: {container: '#TPLinkPlugEditor'}"/> {{ _('GCODE Trigger') }}</label></div></td>

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
plugin_name = "OctoPrint-TPLinkSmartplug"
1515

1616
# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
17-
plugin_version = "2.0.0rc1"
17+
plugin_version = "2.0.0rc2"
1818

1919
# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
2020
# module

0 commit comments

Comments
 (0)