1 """Support for RESTful switches."""
3 from __future__
import annotations
5 from http
import HTTPStatus
10 import voluptuous
as vol
13 DEVICE_CLASSES_SCHEMA,
14 PLATFORM_SCHEMA
as SWITCH_PLATFORM_SCHEMA,
39 TEMPLATE_ENTITY_BASE_SCHEMA,
44 _LOGGER = logging.getLogger(__name__)
45 CONF_BODY_OFF =
"body_off"
46 CONF_BODY_ON =
"body_on"
47 CONF_IS_ON_TEMPLATE =
"is_on_template"
48 CONF_STATE_RESOURCE =
"state_resource"
50 TRIGGER_ENTITY_OPTIONS = (
58 DEFAULT_METHOD =
"post"
59 DEFAULT_BODY_OFF =
"OFF"
60 DEFAULT_BODY_ON =
"ON"
61 DEFAULT_NAME =
"REST Switch"
63 DEFAULT_VERIFY_SSL =
True
65 SUPPORT_REST_METHODS = [
"post",
"put",
"patch"]
67 PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend(
69 **TEMPLATE_ENTITY_BASE_SCHEMA.schema,
70 vol.Required(CONF_RESOURCE): cv.url,
71 vol.Optional(CONF_STATE_RESOURCE): cv.url,
72 vol.Optional(CONF_HEADERS): {cv.string: cv.template},
73 vol.Optional(CONF_PARAMS): {cv.string: cv.template},
74 vol.Optional(CONF_BODY_OFF, default=DEFAULT_BODY_OFF): cv.template,
75 vol.Optional(CONF_BODY_ON, default=DEFAULT_BODY_ON): cv.template,
76 vol.Optional(CONF_IS_ON_TEMPLATE): cv.template,
77 vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): vol.All(
78 vol.Lower, vol.In(SUPPORT_REST_METHODS)
80 vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
81 vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
82 vol.Inclusive(CONF_USERNAME,
"authentication"): cv.string,
83 vol.Inclusive(CONF_PASSWORD,
"authentication"): cv.string,
84 vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,
85 vol.Optional(CONF_AVAILABILITY): cv.template,
93 async_add_entities: AddEntitiesCallback,
94 discovery_info: DiscoveryInfoType |
None =
None,
96 """Set up the RESTful switch."""
97 resource: str = config[CONF_RESOURCE]
98 name = config.get(CONF_NAME)
or template.Template(DEFAULT_NAME, hass)
100 trigger_entity_config = {CONF_NAME: name}
102 for key
in TRIGGER_ENTITY_OPTIONS:
103 if key
not in config:
105 trigger_entity_config[key] = config[key]
108 switch =
RestSwitch(hass, config, trigger_entity_config)
110 req = await switch.get_device_state(hass)
111 if req.status_code >= HTTPStatus.BAD_REQUEST:
112 _LOGGER.error(
"Got non-ok response from resource: %s", req.status_code)
115 except (TypeError, ValueError):
117 "Missing resource or schema in configuration. "
118 "Add http:// or https:// to your URL"
120 except (TimeoutError, httpx.RequestError)
as exc:
121 raise PlatformNotReady(f
"No route to resource/endpoint: {resource}")
from exc
125 """Representation of a switch that can be toggled using REST."""
131 trigger_entity_config: ConfigType,
133 """Initialize the REST switch."""
134 ManualTriggerEntity.__init__(self, hass, trigger_entity_config)
136 auth: httpx.BasicAuth |
None =
None
137 username: str |
None =
None
138 if username := config.get(CONF_USERNAME):
139 password: str = config[CONF_PASSWORD]
140 auth = httpx.BasicAuth(username, password=password)
142 self._resource: str = config[CONF_RESOURCE]
143 self._state_resource: str = config.get(CONF_STATE_RESOURCE)
or self._resource
144 self._method: str = config[CONF_METHOD]
145 self._headers: dict[str, template.Template] |
None = config.get(CONF_HEADERS)
146 self._params: dict[str, template.Template] |
None = config.get(CONF_PARAMS)
148 self._body_on: template.Template = config[CONF_BODY_ON]
149 self._body_off: template.Template = config[CONF_BODY_OFF]
150 self._is_on_template: template.Template |
None = config.get(CONF_IS_ON_TEMPLATE)
151 self._timeout: int = config[CONF_TIMEOUT]
152 self._verify_ssl: bool = config[CONF_VERIFY_SSL]
155 """Handle adding to Home Assistant."""
160 """Turn the device on."""
161 body_on_t = self._body_on.async_render(parse_result=
False)
166 if HTTPStatus.OK <= req.status_code < HTTPStatus.MULTIPLE_CHOICES:
170 "Can't turn on %s. Is resource/endpoint offline?", self._resource
172 except (TimeoutError, httpx.RequestError):
173 _LOGGER.error(
"Error while switching on %s", self._resource)
176 """Turn the device off."""
177 body_off_t = self._body_off.async_render(parse_result=
False)
181 if HTTPStatus.OK <= req.status_code < HTTPStatus.MULTIPLE_CHOICES:
185 "Can't turn off %s. Is resource/endpoint offline?", self._resource
187 except (TimeoutError, httpx.RequestError):
188 _LOGGER.error(
"Error while switching off %s", self._resource)
191 """Send a state update to the device."""
194 rendered_headers = template.render_complex(self._headers, parse_result=
False)
195 rendered_params = template.render_complex(self._params)
197 req: httpx.Response = await getattr(websession, self._method)(
199 auth=self.
_auth_auth,
200 content=bytes(body,
"utf-8"),
201 headers=rendered_headers,
202 params=rendered_params,
203 timeout=self._timeout,
208 """Get the current state, catching errors."""
212 except (TimeoutError, httpx.TimeoutException):
213 _LOGGER.exception(
"Timed out while fetching data")
214 except httpx.RequestError:
215 _LOGGER.exception(
"Error while fetching data")
222 """Get the latest data from REST API and update the state."""
225 rendered_headers = template.render_complex(self._headers, parse_result=
False)
226 rendered_params = template.render_complex(self._params)
228 req = await websession.get(
229 self._state_resource,
230 auth=self.
_auth_auth,
231 headers=rendered_headers,
232 params=rendered_params,
233 timeout=self._timeout,
237 if self._is_on_template
is not None:
238 text = self._is_on_template.async_render_with_possible_json_value(
244 elif text ==
"false":
248 elif text == self._body_on.template:
250 elif text == self._body_off.template:
httpx.Response set_device_state(self, Any body)
None async_added_to_hass(self)
None __init__(self, HomeAssistant hass, ConfigType config, ConfigType trigger_entity_config)
httpx.Response get_device_state(self, HomeAssistant hass)
None async_turn_on(self, **Any kwargs)
None async_turn_off(self, **Any kwargs)
None async_write_ha_state(self)
None _process_manual_data(self, Any|None value=None)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
httpx.AsyncClient get_async_client(HomeAssistant hass, bool verify_ssl=True)