power: Add Home Assistant switch support

* power: Add Home Assistant switch support

Signed-off-by:  Sébastien Jousse <s.jousse@gmail.com>
This commit is contained in:
Sébastien Jousse 2021-05-05 13:14:25 +02:00 committed by GitHub
parent 40d4daa96b
commit c53b95aa93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 1 deletions

View File

@ -172,7 +172,7 @@ gcode:
## `[power]` ## `[power]`
Enables device power control. Currently GPIO (relays), TPLink Smartplug, Enables device power control. Currently GPIO (relays), TPLink Smartplug,
and Tasmota (via http) devices are supported. and Tasmota (via http) devices, HomeAssistant switch are supported.
```ini ```ini
# moonraker.conf # moonraker.conf
@ -258,6 +258,13 @@ password:
# "ID" in the "advanced information" section. # "ID" in the "advanced information" section.
# Provide a user and password with access to "device control" # Provide a user and password with access to "device control"
# and at least the specific device you want to control # and at least the specific device you want to control
address:
port:
device:
token:
# The above options are used for "homeassistant" devices. The
# address should be a valid ip or hostname for the homeassistant controller.
# "device" should be the ID of the switch to control.
``` ```
Below are some potential examples: Below are some potential examples:
@ -294,6 +301,13 @@ type: shelly
address: 192.168.1.125 address: 192.168.1.125
user: user2 user: user2
password: password2 password: password2
[power homeassistant_switch]
type: homeassistant
address: 192.168.1.126
port: 8123
device: switch.1234567890abcdefghij
token: home-assistant-very-long-token
``` ```
It is possible to toggle device power from the Klippy host, this can be done It is possible to toggle device power from the Klippy host, this can be done

View File

@ -38,6 +38,8 @@ class PrinterPower:
dev = Shelly(cfg) dev = Shelly(cfg)
elif dev_type == "homeseer": elif dev_type == "homeseer":
dev = HomeSeer(cfg) dev = HomeSeer(cfg)
elif dev_type == "homeassistant":
dev = HomeAssistant(cfg)
else: else:
raise config.error(f"Unsupported Device Type: {dev_type}") raise config.error(f"Unsupported Device Type: {dev_type}")
self.devices[dev.get_name()] = dev self.devices[dev.get_name()] = dev
@ -620,6 +622,83 @@ class HomeSeer(PowerDevice):
raise self.server.error(msg) from None raise self.server.error(msg) from None
self.state = state self.state = state
class HomeAssistant(PowerDevice):
def __init__(self, config):
super().__init__(config)
self.server = config.get_server()
self.addr = config.get("address")
self.port = config.getint("port", 8123)
self.device = config.get("device")
self.token = config.get("token")
async def _send_homeassistant_command(self, command):
if command == "on":
out_cmd = f"api/services/switch/turn_on"
body = {"entity_id": self.device}
method = "POST"
elif command == "off":
out_cmd = f"api/services/switch/turn_off"
body = {"entity_id": self.device}
method = "POST"
elif command == "info":
out_cmd = f"api/states/{self.device}"
method = "GET"
else:
raise self.server.error(
f"Invalid homeassistant command: {command}")
url = f"http://{self.addr}:{self.port}/{out_cmd}"
headers = {
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json'
}
data = ""
http_client = AsyncHTTPClient()
try:
if (method == "POST"):
response = await http_client.fetch(
url, method="POST", body=json.dumps(body), headers=headers)
data = json_decode(response.body)
else:
response = await http_client.fetch(
url, method="GET", headers=headers)
data = json_decode(response.body)
except Exception:
msg = f"Error sending homeassistant command: {command}"
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': "homeassistant"
}
async def refresh_status(self):
try:
res = await self._send_homeassistant_command("info")
state = res[f"state"]
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:
res = await self._send_homeassistant_command(state)
state = res[0][f"state"]
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 component has multiple configuration sections # The power component has multiple configuration sections
def load_component_multi(config): def load_component_multi(config):
return PrinterPower(config) return PrinterPower(config)