Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Hubitat for power device #834

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions moonraker/components/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ async def request(
retry_pause_time: float = .1,
enable_cache: bool = False,
send_etag: bool = True,
send_if_modified_since: bool = True
send_if_modified_since: bool = True,
validate_ssl_cert: bool = True
) -> HttpResponse:
cache_key = url.split("?", 1)[0]
method = method.upper()
Expand All @@ -105,7 +106,8 @@ async def request(
timeout = 1 + connect_timeout + request_timeout
request = HTTPRequest(url, method, headers, body=body,
request_timeout=request_timeout,
connect_timeout=connect_timeout)
connect_timeout=connect_timeout,
validate_cert=validate_ssl_cert)
err: Optional[BaseException] = None
for i in range(attempts):
if i:
Expand Down
67 changes: 61 additions & 6 deletions moonraker/components/power.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def __init__(self, config: ConfigHelper) -> None:
"shelly": Shelly,
"homeseer": HomeSeer,
"homeassistant": HomeAssistant,
"hubitat": Hubitat,
"loxonev1": Loxonev1,
"rf": RFDevice,
"mqtt": MQTTDevice,
Expand Down Expand Up @@ -451,6 +452,7 @@ def __init__(
self.protocol = config.get("protocol", default_protocol)
if self.port == -1:
self.port = 443 if self.protocol.lower() == "https" else 80
self.validate_ssl_cert = config.getboolean("validate_ssl_cert", True)

async def init_state(self) -> None:
async with self.request_lock:
Expand Down Expand Up @@ -489,7 +491,9 @@ async def _send_http_command(
) -> Dict[str, Any]:
response = await self.client.get(
url, request_timeout=20., attempts=retries,
retry_pause_time=1., enable_cache=False)
retry_pause_time=1., enable_cache=False,
validate_ssl_cert=self.validate_ssl_cert
)
response.raise_for_status(
f"Error sending '{self.type}' command: {command}")
data = cast(dict, response.json())
Expand Down Expand Up @@ -1075,7 +1079,7 @@ async def _send_smartthings_command(self, command: str) -> Dict[str, Any]:
}
response = await self.client.request(
method, url, body=body, headers=headers,
attempts=3, enable_cache=False
attempts=3, enable_cache=False, validate_ssl_cert=self.validate_ssl_cert
)
msg = f"Error sending SmartThings command: {command}"
response.raise_for_status(msg)
Expand Down Expand Up @@ -1153,7 +1157,7 @@ async def _send_homeassistant_command(self, command: str) -> Dict[str, Any]:
data: Dict[str, Any] = {}
response = await self.client.request(
method, url, body=body, headers=headers,
attempts=3, enable_cache=False
attempts=3, enable_cache=False, validate_ssl_cert=self.validate_ssl_cert
)
msg = f"Error sending homeassistant command: {command}"
response.raise_for_status(msg)
Expand All @@ -1171,6 +1175,51 @@ async def _send_power_request(self, state: str) -> str:
res = await self._send_status_request()
return res

class Hubitat(HTTPDevice):
def __init__(self, config: ConfigHelper) -> None:
super().__init__(config)
self.device_id: str = config.get("device_id")
self.maker_id: str = config.get("maker_id")
self.token: str = config.gettemplate("token").render()
self.status_delay: float = config.getfloat("status_delay", 1.)

async def _send_hubitat_command(self, command: str) -> Dict[str, Any]:
if command in ["on", "off"]:
out_cmd = (
f"apps/api/{self.maker_id}/devices/"
f"{self.device_id}/{command}?access_token={self.token}"
)
elif command == "info":
out_cmd = (
f"apps/api/{self.maker_id}/devices/"
f"{self.device_id}?access_token={self.token}"
)
else:
raise self.server.error(f"Invalid hubitat command: {command}")
url = f"{self.protocol}://{quote(self.addr)}:{self.port}/{out_cmd}"
response = await self.client.request(
"GET", url, attempts=3, enable_cache=False, retry_pause_time=1.,
validate_ssl_cert=self.validate_ssl_cert
)
msg = f"Error sending hubitat command: {command}"
response.raise_for_status(msg)
data: Dict[str, Any] = cast(dict, response.json())
return data

async def _send_status_request(self) -> str:
res = await self._send_hubitat_command("info")
attributes = res["attributes"]
for attribute in attributes:
if attribute["name"] == "switch":
return attribute["currentValue"]
raise self.server.error("Switch attribute not found in result")

async def _send_power_request(self, state: str) -> str:
await self._send_hubitat_command(state)
await asyncio.sleep(self.status_delay)
res = await self._send_status_request()
return res

class Loxonev1(HTTPDevice):
def __init__(self, config: ConfigHelper) -> None:
super().__init__(config, default_user="admin",
Expand Down Expand Up @@ -1399,7 +1448,10 @@ async def _send_power_request(self, state: str) -> str:
f"/{self.device_type}s/{quote(self.device_id)}"
f"/{quote(self.state_key)}"
)
ret = await self.client.request("PUT", url, body={"on": new_state})
ret = await self.client.request(
"PUT", url, body={"on": new_state},
validate_ssl_cert=self.validate_ssl_cert
)
resp = cast(List[Dict[str, Dict[str, Any]]], ret.json())
state_url = (
f"/{self.device_type}s/{self.device_id}/{self.state_key}/on"
Expand All @@ -1414,7 +1466,9 @@ async def _send_status_request(self) -> str:
f"{self.protocol}://{quote(self.addr)}:{self.port}/api/{quote(self.user)}"
f"/{self.device_type}s/{quote(self.device_id)}"
)
ret = await self.client.request("GET", url)
ret = await self.client.request(
"GET", url, validate_ssl_cert=self.validate_ssl_cert
)
resp = cast(Dict[str, Dict[str, Any]], ret.json())
return "on" if resp["state"][self.on_state] else "off"

Expand All @@ -1433,7 +1487,8 @@ def __init__(self, config: ConfigHelper,) -> None:

async def _send_generic_request(self, command: str) -> str:
request = self.client.wrap_request(
self.urls[command], request_timeout=20., attempts=3, retry_pause_time=1.
self.urls[command], request_timeout=20., attempts=3, retry_pause_time=1.,
validate_ssl_cert=self.validate_ssl_cert
)
context: Dict[str, Any] = {
"command": command,
Expand Down
Loading