Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Worx Landroid mower."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 import logging
7 
8 import aiohttp
9 import voluptuous as vol
10 
12  PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
13  SensorEntity,
14 )
15 from homeassistant.const import CONF_HOST, CONF_PIN, CONF_TIMEOUT, PERCENTAGE
16 from homeassistant.core import HomeAssistant
17 from homeassistant.helpers.aiohttp_client import async_get_clientsession
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
21 
22 _LOGGER = logging.getLogger(__name__)
23 
24 CONF_ALLOW_UNREACHABLE = "allow_unreachable"
25 
26 DEFAULT_TIMEOUT = 5
27 
28 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
29  {
30  vol.Required(CONF_HOST): cv.string,
31  vol.Required(CONF_PIN): vol.All(vol.Coerce(str), vol.Match(r"\d{4}")),
32  vol.Optional(CONF_ALLOW_UNREACHABLE, default=True): cv.boolean,
33  vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
34  }
35 )
36 
37 ERROR_STATE = [
38  "blade-blocked",
39  "repositioning-error",
40  "wire-bounced",
41  "blade-blocked",
42  "outside-wire",
43  "mower-lifted",
44  "alarm-6",
45  "upside-down",
46  "alarm-8",
47  "collision-sensor-blocked",
48  "mower-tilted",
49  "charge-error",
50  "battery-error",
51 ]
52 
53 
55  hass: HomeAssistant,
56  config: ConfigType,
57  async_add_entities: AddEntitiesCallback,
58  discovery_info: DiscoveryInfoType | None = None,
59 ) -> None:
60  """Set up the Worx Landroid sensors."""
61  for typ in ("battery", "state"):
63 
64 
66  """Implementation of a Worx Landroid sensor."""
67 
68  def __init__(self, sensor, config):
69  """Initialize a Worx Landroid sensor."""
70  self._state_state = None
71  self.sensorsensor = sensor
72  self.hosthost = config.get(CONF_HOST)
73  self.pinpin = config.get(CONF_PIN)
74  self.timeouttimeout = config.get(CONF_TIMEOUT)
75  self.allow_unreachableallow_unreachable = config.get(CONF_ALLOW_UNREACHABLE)
76  self.urlurl = f"http://{self.host}/jsondata.cgi"
77 
78  @property
79  def name(self):
80  """Return the name of the sensor."""
81  return f"worxlandroid-{self.sensor}"
82 
83  @property
84  def native_value(self):
85  """Return the state of the sensor."""
86  return self._state_state
87 
88  @property
90  """Return the unit of measurement of the sensor."""
91  if self.sensorsensor == "battery":
92  return PERCENTAGE
93  return None
94 
95  async def async_update(self) -> None:
96  """Update the sensor data from the mower."""
97  connection_error = False
98 
99  try:
100  session = async_get_clientsession(self.hasshass)
101  async with asyncio.timeout(self.timeouttimeout):
102  auth = aiohttp.helpers.BasicAuth("admin", self.pinpin)
103  mower_response = await session.get(self.urlurl, auth=auth)
104  except (TimeoutError, aiohttp.ClientError):
105  if self.allow_unreachableallow_unreachable is False:
106  _LOGGER.error("Error connecting to mower at %s", self.urlurl)
107 
108  connection_error = True
109 
110  # connection error
111  if connection_error is True and self.allow_unreachableallow_unreachable is False:
112  if self.sensorsensor == "error":
113  self._state_state = "yes"
114  elif self.sensorsensor == "state":
115  self._state_state = "connection-error"
116 
117  # connection success
118  elif connection_error is False:
119  # set the expected content type to be text/html
120  # since the mover incorrectly returns it...
121  data = await mower_response.json(content_type="text/html")
122 
123  # sensor battery
124  if self.sensorsensor == "battery":
125  self._state_state = data["perc_batt"]
126 
127  # sensor error
128  elif self.sensorsensor == "error":
129  self._state_state = "no" if self.get_errorget_error(data) is None else "yes"
130 
131  # sensor state
132  elif self.sensorsensor == "state":
133  self._state_state = self.get_stateget_state(data)
134 
135  elif self.sensorsensor == "error":
136  self._state_state = "no"
137 
138  @staticmethod
139  def get_error(obj):
140  """Get the mower error."""
141  for i, err in enumerate(obj["allarmi"]):
142  if i != 2 and err == 1: # ignore wire bounce errors
143  return ERROR_STATE[i]
144 
145  return None
146 
147  def get_state(self, obj):
148  """Get the state of the mower."""
149  if (state := self.get_errorget_error(obj)) is None:
150  if obj["batteryChargerState"] == "charging":
151  return obj["batteryChargerState"]
152 
153  return obj["state"]
154 
155  return state
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:59
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)