Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for monitoring the rtorrent BitTorrent client API."""
2 
3 from __future__ import annotations
4 
5 import logging
6 import xmlrpc.client
7 
8 import voluptuous as vol
9 
11  PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
12  SensorDeviceClass,
13  SensorEntity,
14  SensorEntityDescription,
15 )
16 from homeassistant.const import (
17  CONF_MONITORED_VARIABLES,
18  CONF_NAME,
19  CONF_URL,
20  STATE_IDLE,
21  UnitOfDataRate,
22 )
23 from homeassistant.core import HomeAssistant
24 from homeassistant.exceptions import PlatformNotReady
26 from homeassistant.helpers.entity_platform import AddEntitiesCallback
27 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
28 
29 _LOGGER = logging.getLogger(__name__)
30 
31 SENSOR_TYPE_CURRENT_STATUS = "current_status"
32 SENSOR_TYPE_DOWNLOAD_SPEED = "download_speed"
33 SENSOR_TYPE_UPLOAD_SPEED = "upload_speed"
34 SENSOR_TYPE_ALL_TORRENTS = "all_torrents"
35 SENSOR_TYPE_STOPPED_TORRENTS = "stopped_torrents"
36 SENSOR_TYPE_COMPLETE_TORRENTS = "complete_torrents"
37 SENSOR_TYPE_UPLOADING_TORRENTS = "uploading_torrents"
38 SENSOR_TYPE_DOWNLOADING_TORRENTS = "downloading_torrents"
39 SENSOR_TYPE_ACTIVE_TORRENTS = "active_torrents"
40 
41 DEFAULT_NAME = "rtorrent"
42 SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
44  key=SENSOR_TYPE_CURRENT_STATUS,
45  name="Status",
46  ),
48  key=SENSOR_TYPE_DOWNLOAD_SPEED,
49  name="Down Speed",
50  device_class=SensorDeviceClass.DATA_RATE,
51  native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
52  ),
54  key=SENSOR_TYPE_UPLOAD_SPEED,
55  name="Up Speed",
56  device_class=SensorDeviceClass.DATA_RATE,
57  native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
58  ),
60  key=SENSOR_TYPE_ALL_TORRENTS,
61  name="All Torrents",
62  ),
64  key=SENSOR_TYPE_STOPPED_TORRENTS,
65  name="Stopped Torrents",
66  ),
68  key=SENSOR_TYPE_COMPLETE_TORRENTS,
69  name="Complete Torrents",
70  ),
72  key=SENSOR_TYPE_UPLOADING_TORRENTS,
73  name="Uploading Torrents",
74  ),
76  key=SENSOR_TYPE_DOWNLOADING_TORRENTS,
77  name="Downloading Torrents",
78  ),
80  key=SENSOR_TYPE_ACTIVE_TORRENTS,
81  name="Active Torrents",
82  ),
83 )
84 
85 SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
86 
87 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
88  {
89  vol.Required(CONF_URL): cv.url,
90  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
91  vol.Optional(CONF_MONITORED_VARIABLES, default=SENSOR_KEYS): vol.All(
92  cv.ensure_list, [vol.In(SENSOR_KEYS)]
93  ),
94  }
95 )
96 
97 
99  hass: HomeAssistant,
100  config: ConfigType,
101  add_entities: AddEntitiesCallback,
102  discovery_info: DiscoveryInfoType | None = None,
103 ) -> None:
104  """Set up the rtorrent sensors."""
105  url = config[CONF_URL]
106  name = config[CONF_NAME]
107 
108  try:
109  rtorrent = xmlrpc.client.ServerProxy(url)
110  except (xmlrpc.client.ProtocolError, ConnectionRefusedError) as ex:
111  _LOGGER.error("Connection to rtorrent daemon failed")
112  raise PlatformNotReady from ex
113  monitored_variables = config[CONF_MONITORED_VARIABLES]
114  entities = [
115  RTorrentSensor(rtorrent, name, description)
116  for description in SENSOR_TYPES
117  if description.key in monitored_variables
118  ]
119 
120  add_entities(entities)
121 
122 
123 def format_speed(speed):
124  """Return a bytes/s measurement as a human readable string."""
125  kb_spd = float(speed) / 1024
126  return round(kb_spd, 2 if kb_spd < 0.1 else 1)
127 
128 
130  """Representation of an rtorrent sensor."""
131 
132  def __init__(
133  self, rtorrent_client, client_name, description: SensorEntityDescription
134  ) -> None:
135  """Initialize the sensor."""
136  self.entity_descriptionentity_description = description
137  self.clientclient = rtorrent_client
138  self.datadata = None
139 
140  self._attr_name_attr_name = f"{client_name} {description.name}"
141  self._attr_available_attr_available = False
142 
143  def update(self):
144  """Get the latest data from rtorrent and updates the state."""
145  multicall = xmlrpc.client.MultiCall(self.clientclient)
146  multicall.throttle.global_up.rate()
147  multicall.throttle.global_down.rate()
148  multicall.d.multicall2("", "main")
149  multicall.d.multicall2("", "stopped")
150  multicall.d.multicall2("", "complete")
151  multicall.d.multicall2("", "seeding", "d.up.rate=")
152  multicall.d.multicall2("", "leeching", "d.down.rate=")
153 
154  try:
155  self.datadata = multicall()
156  self._attr_available_attr_available = True
157  except (xmlrpc.client.ProtocolError, OSError) as ex:
158  _LOGGER.error("Connection to rtorrent failed (%s)", ex)
159  self._attr_available_attr_available = False
160  return
161 
162  upload = self.datadata[0]
163  download = self.datadata[1]
164  all_torrents = self.datadata[2]
165  stopped_torrents = self.datadata[3]
166  complete_torrents = self.datadata[4]
167 
168  uploading_torrents = 0
169  for up_torrent in self.datadata[5]:
170  if up_torrent[0]:
171  uploading_torrents += 1
172 
173  downloading_torrents = 0
174  for down_torrent in self.datadata[6]:
175  if down_torrent[0]:
176  downloading_torrents += 1
177 
178  active_torrents = uploading_torrents + downloading_torrents
179 
180  sensor_type = self.entity_descriptionentity_description.key
181  if sensor_type == SENSOR_TYPE_CURRENT_STATUS:
182  if self.datadata:
183  if upload > 0 and download > 0:
184  self._attr_native_value_attr_native_value = "up_down"
185  elif upload > 0 and download == 0:
186  self._attr_native_value_attr_native_value = "seeding"
187  elif upload == 0 and download > 0:
188  self._attr_native_value_attr_native_value = "downloading"
189  else:
190  self._attr_native_value_attr_native_value = STATE_IDLE
191  else:
192  self._attr_native_value_attr_native_value = None
193 
194  if self.datadata:
195  if sensor_type == SENSOR_TYPE_DOWNLOAD_SPEED:
196  self._attr_native_value_attr_native_value = format_speed(download)
197  elif sensor_type == SENSOR_TYPE_UPLOAD_SPEED:
198  self._attr_native_value_attr_native_value = format_speed(upload)
199  elif sensor_type == SENSOR_TYPE_ALL_TORRENTS:
200  self._attr_native_value_attr_native_value = len(all_torrents)
201  elif sensor_type == SENSOR_TYPE_STOPPED_TORRENTS:
202  self._attr_native_value_attr_native_value = len(stopped_torrents)
203  elif sensor_type == SENSOR_TYPE_COMPLETE_TORRENTS:
204  self._attr_native_value_attr_native_value = len(complete_torrents)
205  elif sensor_type == SENSOR_TYPE_UPLOADING_TORRENTS:
206  self._attr_native_value_attr_native_value = uploading_torrents
207  elif sensor_type == SENSOR_TYPE_DOWNLOADING_TORRENTS:
208  self._attr_native_value_attr_native_value = downloading_torrents
209  elif sensor_type == SENSOR_TYPE_ACTIVE_TORRENTS:
210  self._attr_native_value_attr_native_value = active_torrents
None __init__(self, rtorrent_client, client_name, SensorEntityDescription description)
Definition: sensor.py:134
def add_entities(account, async_add_entities, tracked)
Definition: sensor.py:40
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:103