Home Assistant Unofficial Reference 2024.12.1
switch.py
Go to the documentation of this file.
1 """Support for switch controlled using a telnet connection."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 import logging
7 import telnetlib # pylint: disable=deprecated-module
8 from typing import Any
9 
10 import voluptuous as vol
11 
13  ENTITY_ID_FORMAT,
14  PLATFORM_SCHEMA as SWITCH_PLATFORM_SCHEMA,
15  SwitchEntity,
16 )
17 from homeassistant.const import (
18  CONF_COMMAND_OFF,
19  CONF_COMMAND_ON,
20  CONF_COMMAND_STATE,
21  CONF_NAME,
22  CONF_PORT,
23  CONF_RESOURCE,
24  CONF_SWITCHES,
25  CONF_TIMEOUT,
26  CONF_VALUE_TEMPLATE,
27 )
28 from homeassistant.core import HomeAssistant
30 from homeassistant.helpers.entity_platform import AddEntitiesCallback
31 from homeassistant.helpers.template import Template
32 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
33 
34 _LOGGER = logging.getLogger(__name__)
35 
36 DEFAULT_PORT = 23
37 DEFAULT_TIMEOUT = 0.2
38 
39 SWITCH_SCHEMA = vol.Schema(
40  {
41  vol.Required(CONF_COMMAND_OFF): cv.string,
42  vol.Required(CONF_COMMAND_ON): cv.string,
43  vol.Required(CONF_RESOURCE): cv.string,
44  vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
45  vol.Optional(CONF_COMMAND_STATE): cv.string,
46  vol.Optional(CONF_NAME): cv.string,
47  vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
48  vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): vol.Coerce(float),
49  }
50 )
51 
52 PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend(
53  {vol.Required(CONF_SWITCHES): cv.schema_with_slug_keys(SWITCH_SCHEMA)}
54 )
55 
56 SCAN_INTERVAL = timedelta(seconds=10)
57 
58 
60  hass: HomeAssistant,
61  config: ConfigType,
62  add_entities: AddEntitiesCallback,
63  discovery_info: DiscoveryInfoType | None = None,
64 ) -> None:
65  """Find and return switches controlled by telnet commands."""
66  devices: dict[str, Any] = config[CONF_SWITCHES]
67  switches = []
68 
69  for object_id, device_config in devices.items():
70  switches.append(
72  object_id,
73  device_config[CONF_RESOURCE],
74  device_config[CONF_PORT],
75  device_config.get(CONF_NAME, object_id),
76  device_config[CONF_COMMAND_ON],
77  device_config[CONF_COMMAND_OFF],
78  device_config.get(CONF_COMMAND_STATE),
79  device_config.get(CONF_VALUE_TEMPLATE),
80  device_config[CONF_TIMEOUT],
81  )
82  )
83 
84  if not switches:
85  _LOGGER.error("No switches added")
86  return
87 
88  add_entities(switches)
89 
90 
92  """Representation of a switch that can be toggled using telnet commands."""
93 
94  def __init__(
95  self,
96  object_id: str,
97  resource: str,
98  port: int,
99  friendly_name: str,
100  command_on: str,
101  command_off: str,
102  command_state: str | None,
103  value_template: Template | None,
104  timeout: float,
105  ) -> None:
106  """Initialize the switch."""
107  self.entity_identity_identity_id = ENTITY_ID_FORMAT.format(object_id)
108  self._resource_resource = resource
109  self._port_port = port
110  self._attr_name_attr_name = friendly_name
111  self._attr_is_on_attr_is_on = False
112  self._command_on_command_on = command_on
113  self._command_off_command_off = command_off
114  self._command_state_command_state = command_state
115  self._value_template_value_template = value_template
116  self._timeout_timeout = timeout
117  self._attr_should_poll_attr_should_poll = bool(command_state)
118  self._attr_assumed_state_attr_assumed_state = bool(command_state is None)
119 
120  def _telnet_command(self, command: str) -> str | None:
121  try:
122  telnet = telnetlib.Telnet(self._resource_resource, self._port_port)
123  telnet.write(command.encode("ASCII") + b"\r")
124  response = telnet.read_until(b"\r", timeout=self._timeout_timeout)
125  except OSError as error:
126  _LOGGER.error(
127  'Command "%s" failed with exception: %s', command, repr(error)
128  )
129  return None
130  _LOGGER.debug("telnet response: %s", response.decode("ASCII").strip())
131  return response.decode("ASCII").strip()
132 
133  def update(self) -> None:
134  """Update device state."""
135  if not self._command_state_command_state:
136  return
137  response = self._telnet_command_telnet_command(self._command_state_command_state)
138  if response and self._value_template_value_template:
139  rendered = self._value_template_value_template.render_with_possible_json_value(response)
140  else:
141  _LOGGER.warning("Empty response for command: %s", self._command_state_command_state)
142  return
143  self._attr_is_on_attr_is_on = rendered == "True"
144 
145  def turn_on(self, **kwargs: Any) -> None:
146  """Turn the device on."""
147  self._telnet_command_telnet_command(self._command_on_command_on)
148  if self.assumed_stateassumed_state:
149  self._attr_is_on_attr_is_on = True
150  self.schedule_update_ha_stateschedule_update_ha_state()
151 
152  def turn_off(self, **kwargs: Any) -> None:
153  """Turn the device off."""
154  self._telnet_command_telnet_command(self._command_off_command_off)
155  if self.assumed_stateassumed_state:
156  self._attr_is_on_attr_is_on = False
157  self.schedule_update_ha_stateschedule_update_ha_state()
None __init__(self, str object_id, str resource, int port, str friendly_name, str command_on, str command_off, str|None command_state, Template|None value_template, float timeout)
Definition: switch.py:105
str|None _telnet_command(self, str command)
Definition: switch.py:120
None schedule_update_ha_state(self, bool force_refresh=False)
Definition: entity.py:1244
None add_entities(AsusWrtRouter router, AddEntitiesCallback async_add_entities, set[str] tracked)
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: switch.py:64