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

Add lacrosse to strict-typing #86527

Merged
merged 1 commit into from
Jan 24, 2023
Merged
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
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ homeassistant.components.jewish_calendar.*
homeassistant.components.kaleidescape.*
homeassistant.components.knx.*
homeassistant.components.kraken.*
homeassistant.components.lacrosse.*
homeassistant.components.lacrosse_view.*
homeassistant.components.lametric.*
homeassistant.components.laundrify.*
Expand Down
65 changes: 37 additions & 28 deletions homeassistant/components/lacrosse/sensor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Support for LaCrosse sensor components."""
from __future__ import annotations

from datetime import timedelta
from datetime import datetime, timedelta
import logging
from typing import Any

import pylacrosse
from serial import SerialException
Expand All @@ -25,7 +26,7 @@
PERCENTAGE,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback
Expand All @@ -44,7 +45,7 @@
CONF_TOGGLE_MASK = "toggle_mask"

DEFAULT_DEVICE = "/dev/ttyUSB0"
DEFAULT_BAUD = "57600"
DEFAULT_BAUD = 57600
DEFAULT_EXPIRE_AFTER = 300

TYPES = ["battery", "humidity", "temperature"]
Expand All @@ -61,7 +62,7 @@
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_SENSORS): cv.schema_with_slug_keys(SENSOR_SCHEMA),
vol.Optional(CONF_BAUD, default=DEFAULT_BAUD): cv.string,
vol.Optional(CONF_BAUD, default=DEFAULT_BAUD): cv.positive_int,
vol.Optional(CONF_DATARATE): cv.positive_int,
vol.Optional(CONF_DEVICE, default=DEFAULT_DEVICE): cv.string,
vol.Optional(CONF_FREQUENCY): cv.positive_int,
Expand All @@ -79,9 +80,9 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the LaCrosse sensors."""
usb_device = config[CONF_DEVICE]
baud = int(config[CONF_BAUD])
expire_after = config.get(CONF_EXPIRE_AFTER)
usb_device: str = config[CONF_DEVICE]
baud: int = config[CONF_BAUD]
expire_after: int | None = config.get(CONF_EXPIRE_AFTER)

_LOGGER.debug("%s %s", usb_device, baud)

Expand All @@ -92,7 +93,7 @@ def setup_platform(
_LOGGER.warning("Unable to open serial port: %s", exc)
return

hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, lambda event: lacrosse.close())
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, lambda event: lacrosse.close()) # type: ignore[no-any-return]

if CONF_JEELINK_LED in config:
lacrosse.led_mode_state(config.get(CONF_JEELINK_LED))
Expand All @@ -107,13 +108,13 @@ def setup_platform(

lacrosse.start_scan()

sensors = []
sensors: list[LaCrosseSensor] = []
for device, device_config in config[CONF_SENSORS].items():
_LOGGER.debug("%s %s", device, device_config)

typ = device_config.get(CONF_TYPE)
typ: str = device_config[CONF_TYPE]
sensor_class = TYPE_CLASSES[typ]
name = device_config.get(CONF_NAME, device)
name: str = device_config.get(CONF_NAME, device)

sensors.append(
sensor_class(hass, lacrosse, device, name, expire_after, device_config)
Expand All @@ -125,36 +126,45 @@ def setup_platform(
class LaCrosseSensor(SensorEntity):
"""Implementation of a Lacrosse sensor."""

_temperature = None
_humidity = None
_low_battery = None
_new_battery = None

def __init__(self, hass, lacrosse, device_id, name, expire_after, config):
_temperature: float | None = None
_humidity: int | None = None
_low_battery: bool | None = None
_new_battery: bool | None = None

def __init__(
self,
hass: HomeAssistant,
lacrosse: pylacrosse.LaCrosse,
device_id: str,
name: str,
expire_after: int | None,
config: ConfigType,
) -> None:
"""Initialize the sensor."""
self.hass = hass
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, device_id, hass=hass
)
self._config = config
self._value = None
self._expire_after = expire_after
self._expiration_trigger = None
self._expiration_trigger: CALLBACK_TYPE | None = None
self._attr_name = name

lacrosse.register_callback(
int(self._config["id"]), self._callback_lacrosse, None
)

@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return {
"low_battery": self._low_battery,
"new_battery": self._new_battery,
}

def _callback_lacrosse(self, lacrosse_sensor, user_data):
def _callback_lacrosse(
self, lacrosse_sensor: pylacrosse.LaCrosseSensor, user_data: None
) -> None:
"""Handle a function that is called from pylacrosse with new values."""
if self._expire_after is not None and self._expire_after > 0:
# Reset old trigger
Expand All @@ -175,10 +185,9 @@ def _callback_lacrosse(self, lacrosse_sensor, user_data):
self._new_battery = lacrosse_sensor.new_battery

@callback
def value_is_expired(self, *_):
def value_is_expired(self, *_: datetime) -> None:
"""Triggered when value is expired."""
self._expiration_trigger = None
self._value = None
self.async_write_ha_state()


Expand All @@ -190,7 +199,7 @@ class LaCrosseTemperature(LaCrosseSensor):
_attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS

@property
def native_value(self):
def native_value(self) -> float | None:
"""Return the state of the sensor."""
return self._temperature

Expand All @@ -203,7 +212,7 @@ class LaCrosseHumidity(LaCrosseSensor):
_attr_icon = "mdi:water-percent"

@property
def native_value(self):
def native_value(self) -> int | None:
"""Return the state of the sensor."""
return self._humidity

Expand All @@ -212,7 +221,7 @@ class LaCrosseBattery(LaCrosseSensor):
"""Implementation of a Lacrosse battery sensor."""

@property
def native_value(self):
def native_value(self) -> str | None:
"""Return the state of the sensor."""
if self._low_battery is None:
return None
Expand All @@ -221,7 +230,7 @@ def native_value(self):
return "ok"

@property
def icon(self):
def icon(self) -> str:
"""Icon to use in the frontend."""
if self._low_battery is None:
return "mdi:battery-unknown"
Expand All @@ -230,7 +239,7 @@ def icon(self):
return "mdi:battery"


TYPE_CLASSES = {
TYPE_CLASSES: dict[str, type[LaCrosseSensor]] = {
"temperature": LaCrosseTemperature,
"humidity": LaCrosseHumidity,
"battery": LaCrosseBattery,
Expand Down
10 changes: 10 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1504,6 +1504,16 @@ disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true

[mypy-homeassistant.components.lacrosse.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true

[mypy-homeassistant.components.lacrosse_view.*]
check_untyped_defs = true
disallow_incomplete_defs = true
Expand Down