diff --git a/docs/configuration.md b/docs/configuration.md index f2a809e..272cdaa 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -191,7 +191,8 @@ and Tasmota (via http) devices are supported. [power device_name] type: gpio -# The type of device. Can be either gpio, tplink_smartplug or tasmota. +# The type of device. Can be either gpio, tplink_smartplug, tasmota +# or homeseer. # This parameter must be provided. off_when_shutdown: False # If set to True the device will be powered off when Klipper enters @@ -248,7 +249,19 @@ output_id: # If password is set but user is empty the default user "admin" will be used # Provided an output_id (relay id) if the Shelly device supports # more than one (default is 0). - +address: +device: +user: +password: +# The above options are used for "homeseer" devices. The +# address should be a valid ip or hostname for the homeseer controller. +# "device" should be the ID of the device to control. +# To find out the ID, in the HomeSeer UI, click on the device you want to +# control (Make sure to click the sub-device that actually has On/Off +# buttons). And then go to the "status/graphics" tab and it should list +# "ID" in the "advanced information" section. +# Provide a user and password with access to "device control" +# and at least the specific device you want to control ``` Below are some potential examples: diff --git a/moonraker/plugins/power.py b/moonraker/plugins/power.py index b56a703..dfac1b1 100644 --- a/moonraker/plugins/power.py +++ b/moonraker/plugins/power.py @@ -36,6 +36,8 @@ class PrinterPower: dev = Tasmota(cfg) elif dev_type == "shelly": dev = Shelly(cfg) + elif dev_type == "homeseer": + dev = HomeSeer(cfg) else: raise config.error(f"Unsupported Device Type: {dev_type}") self.devices[dev.get_name()] = dev @@ -522,6 +524,65 @@ class Shelly(PowerDevice): raise self.server.error(msg) from None self.state = "on" if state else "off" +class HomeSeer(PowerDevice): + def __init__(self, config): + super().__init__(config) + self.server = config.get_server() + self.addr = config.get("address") + self.device = config.getint("device") + self.user = config.get("user", "admin") + self.password = config.get("password", "") + + async def _send_homeseer(self, request, additional = ""): + url = (f"http://{self.user}:{self.password}@{self.addr}" + f"/JSON?user={self.user}&pass={self.password}" + f"&request={request}&ref={self.device}&{additional}") + data = "" + http_client = AsyncHTTPClient() + try: + response = await http_client.fetch(url) + data = json_decode(response.body) + except Exception: + msg = f"Error sending HomeSeer command: {request}" + logging.exception(msg) + raise self.server.error(msg) + return data + + async def initialize(self): + await self.refresh_status() + + def get_device_info(self): + return { + **super().get_device_info(), + 'type': "homeseer" + } + + async def refresh_status(self): + try: + res = await self._send_homeseer("getstatus") + state = res[f"Devices"][0]["status"].lower() + except Exception: + self.state = "error" + msg = f"Error Refeshing Device Status: {self.name}" + logging.exception(msg) + raise self.server.error(msg) from None + self.state = state + + async def set_power(self, state): + try: + if state == "on": + state_hs = "On" + elif state == "off": + state_hs = "Off" + res = await self._send_homeseer("controldevicebylabel", + f"label={state_hs}") + except Exception: + self.state = "error" + msg = f"Error Setting Device Status: {self.name} to {state}" + logging.exception(msg) + raise self.server.error(msg) from None + self.state = state + # The power plugin has multiple configuration sections def load_plugin_multi(config): return PrinterPower(config)