Enable strict type checks for onewire (#50422)
This commit is contained in:
parent
efa5c59559
commit
d6c99a3db9
13 changed files with 202 additions and 84 deletions
|
@ -31,6 +31,7 @@ homeassistant.components.media_player.*
|
||||||
homeassistant.components.nam.*
|
homeassistant.components.nam.*
|
||||||
homeassistant.components.notify.*
|
homeassistant.components.notify.*
|
||||||
homeassistant.components.number.*
|
homeassistant.components.number.*
|
||||||
|
homeassistant.components.onewire.*
|
||||||
homeassistant.components.persistent_notification.*
|
homeassistant.components.persistent_notification.*
|
||||||
homeassistant.components.proximity.*
|
homeassistant.components.proximity.*
|
||||||
homeassistant.components.recorder.purge
|
homeassistant.components.recorder.purge
|
||||||
|
|
|
@ -13,7 +13,7 @@ from .onewirehub import CannotConnect, OneWireHub
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||||
"""Set up a 1-Wire proxy for a config entry."""
|
"""Set up a 1-Wire proxy for a config entry."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||||
config_entry, PLATFORMS
|
config_entry, PLATFORMS
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
"""Support for 1-Wire binary sensors."""
|
"""Support for 1-Wire binary sensors."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_TYPE
|
from homeassistant.const import CONF_TYPE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import CONF_TYPE_OWSERVER, DOMAIN, SENSOR_TYPE_SENSED
|
from .const import CONF_TYPE_OWSERVER, DOMAIN, SENSOR_TYPE_SENSED
|
||||||
from .onewire_entities import OneWireProxyEntity
|
from .model import DeviceComponentDescription
|
||||||
|
from .onewire_entities import OneWireBaseEntity, OneWireProxyEntity
|
||||||
from .onewirehub import OneWireHub
|
from .onewirehub import OneWireHub
|
||||||
|
|
||||||
DEVICE_BINARY_SENSORS = {
|
DEVICE_BINARY_SENSORS: dict[str, list[DeviceComponentDescription]] = {
|
||||||
# Family : { path, sensor_type }
|
# Family : { path, sensor_type }
|
||||||
"12": [
|
"12": [
|
||||||
{
|
{
|
||||||
|
@ -77,7 +84,11 @@ DEVICE_BINARY_SENSORS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
"""Set up 1-Wire platform."""
|
"""Set up 1-Wire platform."""
|
||||||
# Only OWServer implementation works with binary sensors
|
# Only OWServer implementation works with binary sensors
|
||||||
if config_entry.data[CONF_TYPE] == CONF_TYPE_OWSERVER:
|
if config_entry.data[CONF_TYPE] == CONF_TYPE_OWSERVER:
|
||||||
|
@ -87,9 +98,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
|
|
||||||
def get_entities(onewirehub: OneWireHub):
|
def get_entities(onewirehub: OneWireHub) -> list[OneWireBaseEntity]:
|
||||||
"""Get a list of entities."""
|
"""Get a list of entities."""
|
||||||
entities = []
|
if not onewirehub.devices:
|
||||||
|
return []
|
||||||
|
|
||||||
|
entities: list[OneWireBaseEntity] = []
|
||||||
|
|
||||||
for device in onewirehub.devices:
|
for device in onewirehub.devices:
|
||||||
family = device["family"]
|
family = device["family"]
|
||||||
|
@ -98,7 +112,7 @@ def get_entities(onewirehub: OneWireHub):
|
||||||
|
|
||||||
if family not in DEVICE_BINARY_SENSORS:
|
if family not in DEVICE_BINARY_SENSORS:
|
||||||
continue
|
continue
|
||||||
device_info = {
|
device_info: DeviceInfo = {
|
||||||
"identifiers": {(DOMAIN, device_id)},
|
"identifiers": {(DOMAIN, device_id)},
|
||||||
"manufacturer": "Maxim Integrated",
|
"manufacturer": "Maxim Integrated",
|
||||||
"model": device_type,
|
"model": device_type,
|
||||||
|
@ -126,6 +140,6 @@ class OneWireProxyBinarySensor(OneWireProxyEntity, BinarySensorEntity):
|
||||||
"""Implementation of a 1-Wire binary sensor."""
|
"""Implementation of a 1-Wire binary sensor."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self) -> bool:
|
||||||
"""Return true if sensor is on."""
|
"""Return true if sensor is on."""
|
||||||
return self._state
|
return bool(self._state)
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
"""Config flow for 1-Wire component."""
|
"""Config flow for 1-Wire component."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlow
|
from homeassistant.config_entries import ConfigFlow
|
||||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
|
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_MOUNT_DIR,
|
CONF_MOUNT_DIR,
|
||||||
|
@ -32,7 +37,9 @@ DATA_SCHEMA_MOUNTDIR = vol.Schema(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def validate_input_owserver(hass: HomeAssistant, data):
|
async def validate_input_owserver(
|
||||||
|
hass: HomeAssistant, data: dict[str, Any]
|
||||||
|
) -> dict[str, str]:
|
||||||
"""Validate the user input allows us to connect.
|
"""Validate the user input allows us to connect.
|
||||||
|
|
||||||
Data has the keys from DATA_SCHEMA_OWSERVER with values provided by the user.
|
Data has the keys from DATA_SCHEMA_OWSERVER with values provided by the user.
|
||||||
|
@ -49,7 +56,9 @@ async def validate_input_owserver(hass: HomeAssistant, data):
|
||||||
return {"title": host}
|
return {"title": host}
|
||||||
|
|
||||||
|
|
||||||
def is_duplicate_owserver_entry(hass: HomeAssistant, user_input):
|
def is_duplicate_owserver_entry(
|
||||||
|
hass: HomeAssistant, user_input: dict[str, Any]
|
||||||
|
) -> bool:
|
||||||
"""Check existing entries for matching host and port."""
|
"""Check existing entries for matching host and port."""
|
||||||
for config_entry in hass.config_entries.async_entries(DOMAIN):
|
for config_entry in hass.config_entries.async_entries(DOMAIN):
|
||||||
if (
|
if (
|
||||||
|
@ -61,7 +70,9 @@ def is_duplicate_owserver_entry(hass: HomeAssistant, user_input):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def validate_input_mount_dir(hass: HomeAssistant, data):
|
async def validate_input_mount_dir(
|
||||||
|
hass: HomeAssistant, data: dict[str, Any]
|
||||||
|
) -> dict[str, str]:
|
||||||
"""Validate the user input allows us to connect.
|
"""Validate the user input allows us to connect.
|
||||||
|
|
||||||
Data has the keys from DATA_SCHEMA_MOUNTDIR with values provided by the user.
|
Data has the keys from DATA_SCHEMA_MOUNTDIR with values provided by the user.
|
||||||
|
@ -82,16 +93,18 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
"""Initialize 1-Wire config flow."""
|
"""Initialize 1-Wire config flow."""
|
||||||
self.onewire_config = {}
|
self.onewire_config: dict[str, Any] = {}
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Handle 1-Wire config flow start.
|
"""Handle 1-Wire config flow start.
|
||||||
|
|
||||||
Let user manually input configuration.
|
Let user manually input configuration.
|
||||||
"""
|
"""
|
||||||
errors = {}
|
errors: dict[str, str] = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
self.onewire_config.update(user_input)
|
self.onewire_config.update(user_input)
|
||||||
if CONF_TYPE_OWSERVER == user_input[CONF_TYPE]:
|
if CONF_TYPE_OWSERVER == user_input[CONF_TYPE]:
|
||||||
|
@ -105,7 +118,9 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_owserver(self, user_input=None):
|
async def async_step_owserver(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Handle OWServer configuration."""
|
"""Handle OWServer configuration."""
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input:
|
if user_input:
|
||||||
|
@ -130,7 +145,9 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_mount_dir(self, user_input=None):
|
async def async_step_mount_dir(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
"""Handle SysBus configuration."""
|
"""Handle SysBus configuration."""
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input:
|
if user_input:
|
||||||
|
@ -157,7 +174,7 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_import(self, platform_config):
|
async def async_step_import(self, platform_config: dict[str, Any]) -> FlowResult:
|
||||||
"""Handle import configuration from YAML."""
|
"""Handle import configuration from YAML."""
|
||||||
# OWServer
|
# OWServer
|
||||||
if platform_config[CONF_TYPE] == CONF_TYPE_OWSERVER:
|
if platform_config[CONF_TYPE] == CONF_TYPE_OWSERVER:
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Constants for 1-Wire component."""
|
"""Constants for 1-Wire component."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
|
@ -44,7 +46,7 @@ SENSOR_TYPE_WETNESS = "wetness"
|
||||||
SWITCH_TYPE_LATCH = "latch"
|
SWITCH_TYPE_LATCH = "latch"
|
||||||
SWITCH_TYPE_PIO = "pio"
|
SWITCH_TYPE_PIO = "pio"
|
||||||
|
|
||||||
SENSOR_TYPES = {
|
SENSOR_TYPES: dict[str, list[str | None]] = {
|
||||||
# SensorType: [ Unit, DeviceClass ]
|
# SensorType: [ Unit, DeviceClass ]
|
||||||
SENSOR_TYPE_TEMPERATURE: [TEMP_CELSIUS, DEVICE_CLASS_TEMPERATURE],
|
SENSOR_TYPE_TEMPERATURE: [TEMP_CELSIUS, DEVICE_CLASS_TEMPERATURE],
|
||||||
SENSOR_TYPE_HUMIDITY: [PERCENTAGE, DEVICE_CLASS_HUMIDITY],
|
SENSOR_TYPE_HUMIDITY: [PERCENTAGE, DEVICE_CLASS_HUMIDITY],
|
||||||
|
|
21
homeassistant/components/onewire/model.py
Normal file
21
homeassistant/components/onewire/model.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
"""Type definitions for 1-Wire integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceComponentDescription(TypedDict, total=False):
|
||||||
|
"""Device component description class."""
|
||||||
|
|
||||||
|
path: str
|
||||||
|
name: str
|
||||||
|
type: str
|
||||||
|
default_disabled: bool
|
||||||
|
|
||||||
|
|
||||||
|
class OWServerDeviceDescription(TypedDict):
|
||||||
|
"""OWServer device description class."""
|
||||||
|
|
||||||
|
path: str
|
||||||
|
family: str
|
||||||
|
type: str
|
|
@ -7,6 +7,7 @@ from typing import Any
|
||||||
from pyownet import protocol
|
from pyownet import protocol
|
||||||
|
|
||||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||||
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
SENSOR_TYPE_COUNT,
|
SENSOR_TYPE_COUNT,
|
||||||
|
@ -15,6 +16,7 @@ from .const import (
|
||||||
SWITCH_TYPE_LATCH,
|
SWITCH_TYPE_LATCH,
|
||||||
SWITCH_TYPE_PIO,
|
SWITCH_TYPE_PIO,
|
||||||
)
|
)
|
||||||
|
from .model import DeviceComponentDescription
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -24,13 +26,13 @@ class OneWireBaseEntity(Entity):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name,
|
name: str,
|
||||||
device_file,
|
device_file: str,
|
||||||
entity_type: str,
|
entity_type: str,
|
||||||
entity_name: str = None,
|
entity_name: str,
|
||||||
device_info: DeviceInfo | None = None,
|
device_info: DeviceInfo,
|
||||||
default_disabled: bool = False,
|
default_disabled: bool,
|
||||||
unique_id: str = None,
|
unique_id: str,
|
||||||
):
|
):
|
||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
self._name = f"{name} {entity_name or entity_type.capitalize()}"
|
self._name = f"{name} {entity_name or entity_type.capitalize()}"
|
||||||
|
@ -39,10 +41,10 @@ class OneWireBaseEntity(Entity):
|
||||||
self._device_class = SENSOR_TYPES[entity_type][1]
|
self._device_class = SENSOR_TYPES[entity_type][1]
|
||||||
self._unit_of_measurement = SENSOR_TYPES[entity_type][0]
|
self._unit_of_measurement = SENSOR_TYPES[entity_type][0]
|
||||||
self._device_info = device_info
|
self._device_info = device_info
|
||||||
self._state = None
|
self._state: StateType = None
|
||||||
self._value_raw = None
|
self._value_raw: float | None = None
|
||||||
self._default_disabled = default_disabled
|
self._default_disabled = default_disabled
|
||||||
self._unique_id = unique_id or device_file
|
self._unique_id = unique_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str | None:
|
def name(self) -> str | None:
|
||||||
|
@ -84,7 +86,7 @@ class OneWireProxyEntity(OneWireBaseEntity):
|
||||||
device_name: str,
|
device_name: str,
|
||||||
device_info: DeviceInfo,
|
device_info: DeviceInfo,
|
||||||
entity_path: str,
|
entity_path: str,
|
||||||
entity_specs: dict[str, Any],
|
entity_specs: DeviceComponentDescription,
|
||||||
owproxy: protocol._Proxy,
|
owproxy: protocol._Proxy,
|
||||||
):
|
):
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
|
@ -99,31 +101,30 @@ class OneWireProxyEntity(OneWireBaseEntity):
|
||||||
)
|
)
|
||||||
self._owproxy = owproxy
|
self._owproxy = owproxy
|
||||||
|
|
||||||
def _read_value_ownet(self):
|
def _read_value_ownet(self) -> str:
|
||||||
"""Read a value from the owserver."""
|
"""Read a value from the owserver."""
|
||||||
return self._owproxy.read(self._device_file).decode().lstrip()
|
read_bytes: bytes = self._owproxy.read(self._device_file)
|
||||||
|
return read_bytes.decode().lstrip()
|
||||||
|
|
||||||
def _write_value_ownet(self, value: bytes):
|
def _write_value_ownet(self, value: bytes) -> None:
|
||||||
"""Write a value to the owserver."""
|
"""Write a value to the owserver."""
|
||||||
return self._owproxy.write(self._device_file, value)
|
self._owproxy.write(self._device_file, value)
|
||||||
|
|
||||||
def update(self):
|
def update(self) -> None:
|
||||||
"""Get the latest data from the device."""
|
"""Get the latest data from the device."""
|
||||||
value = None
|
|
||||||
try:
|
try:
|
||||||
self._value_raw = float(self._read_value_ownet())
|
self._value_raw = float(self._read_value_ownet())
|
||||||
except protocol.Error as exc:
|
except protocol.Error as exc:
|
||||||
_LOGGER.error("Owserver failure in read(), got: %s", exc)
|
_LOGGER.error("Owserver failure in read(), got: %s", exc)
|
||||||
|
self._state = None
|
||||||
else:
|
else:
|
||||||
if self._entity_type == SENSOR_TYPE_COUNT:
|
if self._entity_type == SENSOR_TYPE_COUNT:
|
||||||
value = int(self._value_raw)
|
self._state = int(self._value_raw)
|
||||||
elif self._entity_type in [
|
elif self._entity_type in [
|
||||||
SENSOR_TYPE_SENSED,
|
SENSOR_TYPE_SENSED,
|
||||||
SWITCH_TYPE_LATCH,
|
SWITCH_TYPE_LATCH,
|
||||||
SWITCH_TYPE_PIO,
|
SWITCH_TYPE_PIO,
|
||||||
]:
|
]:
|
||||||
value = int(self._value_raw) == 1
|
self._state = int(self._value_raw) == 1
|
||||||
else:
|
else:
|
||||||
value = round(self._value_raw, 1)
|
self._state = round(self._value_raw, 1)
|
||||||
|
|
||||||
self._state = value
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Hub for communication with 1-Wire server or mount_dir."""
|
"""Hub for communication with 1-Wire server or mount_dir."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from pi1wire import Pi1Wire
|
from pi1wire import Pi1Wire
|
||||||
|
@ -10,6 +12,7 @@ from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from .const import CONF_MOUNT_DIR, CONF_TYPE_OWSERVER, CONF_TYPE_SYSBUS
|
from .const import CONF_MOUNT_DIR, CONF_TYPE_OWSERVER, CONF_TYPE_SYSBUS
|
||||||
|
from .model import OWServerDeviceDescription
|
||||||
|
|
||||||
DEVICE_COUPLERS = {
|
DEVICE_COUPLERS = {
|
||||||
# Family : [branches]
|
# Family : [branches]
|
||||||
|
@ -23,10 +26,10 @@ class OneWireHub:
|
||||||
def __init__(self, hass: HomeAssistant):
|
def __init__(self, hass: HomeAssistant):
|
||||||
"""Initialize."""
|
"""Initialize."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.type: str = None
|
self.type: str | None = None
|
||||||
self.pi1proxy: Pi1Wire = None
|
self.pi1proxy: Pi1Wire | None = None
|
||||||
self.owproxy: protocol._Proxy = None
|
self.owproxy: protocol._Proxy | None = None
|
||||||
self.devices = None
|
self.devices: list | None = None
|
||||||
|
|
||||||
async def connect(self, host: str, port: int) -> None:
|
async def connect(self, host: str, port: int) -> None:
|
||||||
"""Connect to the owserver host."""
|
"""Connect to the owserver host."""
|
||||||
|
@ -54,10 +57,11 @@ class OneWireHub:
|
||||||
await self.connect(host, port)
|
await self.connect(host, port)
|
||||||
await self.discover_devices()
|
await self.discover_devices()
|
||||||
|
|
||||||
async def discover_devices(self):
|
async def discover_devices(self) -> None:
|
||||||
"""Discover all devices."""
|
"""Discover all devices."""
|
||||||
if self.devices is None:
|
if self.devices is None:
|
||||||
if self.type == CONF_TYPE_SYSBUS:
|
if self.type == CONF_TYPE_SYSBUS:
|
||||||
|
assert self.pi1proxy
|
||||||
self.devices = await self.hass.async_add_executor_job(
|
self.devices = await self.hass.async_add_executor_job(
|
||||||
self.pi1proxy.find_all_sensors
|
self.pi1proxy.find_all_sensors
|
||||||
)
|
)
|
||||||
|
@ -65,11 +69,13 @@ class OneWireHub:
|
||||||
self.devices = await self.hass.async_add_executor_job(
|
self.devices = await self.hass.async_add_executor_job(
|
||||||
self._discover_devices_owserver
|
self._discover_devices_owserver
|
||||||
)
|
)
|
||||||
return self.devices
|
|
||||||
|
|
||||||
def _discover_devices_owserver(self, path="/"):
|
def _discover_devices_owserver(
|
||||||
|
self, path: str = "/"
|
||||||
|
) -> list[OWServerDeviceDescription]:
|
||||||
"""Discover all owserver devices."""
|
"""Discover all owserver devices."""
|
||||||
devices = []
|
devices = []
|
||||||
|
assert self.owproxy
|
||||||
for device_path in self.owproxy.dir(path):
|
for device_path in self.owproxy.dir(path):
|
||||||
device_family = self.owproxy.read(f"{device_path}family").decode()
|
device_family = self.owproxy.read(f"{device_path}family").decode()
|
||||||
device_type = self.owproxy.read(f"{device_path}type").decode()
|
device_type = self.owproxy.read(f"{device_path}type").decode()
|
||||||
|
|
|
@ -4,15 +4,20 @@ from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from types import MappingProxyType
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from pi1wire import InvalidCRCException, UnsupportResponseException
|
from pi1wire import InvalidCRCException, OneWireInterface, UnsupportResponseException
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
|
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.typing import DiscoveryInfoType, StateType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_MOUNT_DIR,
|
CONF_MOUNT_DIR,
|
||||||
|
@ -32,12 +37,13 @@ from .const import (
|
||||||
SENSOR_TYPE_VOLTAGE,
|
SENSOR_TYPE_VOLTAGE,
|
||||||
SENSOR_TYPE_WETNESS,
|
SENSOR_TYPE_WETNESS,
|
||||||
)
|
)
|
||||||
|
from .model import DeviceComponentDescription
|
||||||
from .onewire_entities import OneWireBaseEntity, OneWireProxyEntity
|
from .onewire_entities import OneWireBaseEntity, OneWireProxyEntity
|
||||||
from .onewirehub import OneWireHub
|
from .onewirehub import OneWireHub
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEVICE_SENSORS = {
|
DEVICE_SENSORS: dict[str, list[DeviceComponentDescription]] = {
|
||||||
# Family : { SensorType: owfs path }
|
# Family : { SensorType: owfs path }
|
||||||
"10": [
|
"10": [
|
||||||
{"path": "temperature", "name": "Temperature", "type": SENSOR_TYPE_TEMPERATURE}
|
{"path": "temperature", "name": "Temperature", "type": SENSOR_TYPE_TEMPERATURE}
|
||||||
|
@ -145,7 +151,7 @@ DEVICE_SUPPORT_SYSBUS = ["10", "22", "28", "3B", "42"]
|
||||||
# These can only be read by OWFS. Currently this driver only supports them
|
# These can only be read by OWFS. Currently this driver only supports them
|
||||||
# via owserver (network protocol)
|
# via owserver (network protocol)
|
||||||
|
|
||||||
HOBBYBOARD_EF = {
|
HOBBYBOARD_EF: dict[str, list[DeviceComponentDescription]] = {
|
||||||
"HobbyBoards_EF": [
|
"HobbyBoards_EF": [
|
||||||
{
|
{
|
||||||
"path": "humidity/humidity_corrected",
|
"path": "humidity/humidity_corrected",
|
||||||
|
@ -189,7 +195,7 @@ HOBBYBOARD_EF = {
|
||||||
|
|
||||||
# 7E sensors are special sensors by Embedded Data Systems
|
# 7E sensors are special sensors by Embedded Data Systems
|
||||||
|
|
||||||
EDS_SENSORS = {
|
EDS_SENSORS: dict[str, list[DeviceComponentDescription]] = {
|
||||||
"EDS0068": [
|
"EDS0068": [
|
||||||
{
|
{
|
||||||
"path": "EDS0068/temperature",
|
"path": "EDS0068/temperature",
|
||||||
|
@ -225,7 +231,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_sensor_types(device_sub_type):
|
def get_sensor_types(device_sub_type: str) -> dict[str, Any]:
|
||||||
"""Return the proper info array for the device type."""
|
"""Return the proper info array for the device type."""
|
||||||
if "HobbyBoard" in device_sub_type:
|
if "HobbyBoard" in device_sub_type:
|
||||||
return HOBBYBOARD_EF
|
return HOBBYBOARD_EF
|
||||||
|
@ -234,7 +240,12 @@ def get_sensor_types(device_sub_type):
|
||||||
return DEVICE_SENSORS
|
return DEVICE_SENSORS
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
async def async_setup_platform(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config: dict[str, Any],
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
discovery_info: DiscoveryInfoType | None = None,
|
||||||
|
) -> None:
|
||||||
"""Old way of setting up 1-Wire platform."""
|
"""Old way of setting up 1-Wire platform."""
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Loading 1-Wire via platform setup is deprecated. "
|
"Loading 1-Wire via platform setup is deprecated. "
|
||||||
|
@ -253,7 +264,11 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
"""Set up 1-Wire platform."""
|
"""Set up 1-Wire platform."""
|
||||||
onewirehub = hass.data[DOMAIN][config_entry.entry_id]
|
onewirehub = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
entities = await hass.async_add_executor_job(
|
entities = await hass.async_add_executor_job(
|
||||||
|
@ -262,9 +277,14 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
|
|
||||||
def get_entities(onewirehub: OneWireHub, config):
|
def get_entities(
|
||||||
|
onewirehub: OneWireHub, config: MappingProxyType[str, Any]
|
||||||
|
) -> list[OneWireBaseEntity]:
|
||||||
"""Get a list of entities."""
|
"""Get a list of entities."""
|
||||||
entities = []
|
if not onewirehub.devices:
|
||||||
|
return []
|
||||||
|
|
||||||
|
entities: list[OneWireBaseEntity] = []
|
||||||
device_names = {}
|
device_names = {}
|
||||||
if CONF_NAMES in config and isinstance(config[CONF_NAMES], dict):
|
if CONF_NAMES in config and isinstance(config[CONF_NAMES], dict):
|
||||||
device_names = config[CONF_NAMES]
|
device_names = config[CONF_NAMES]
|
||||||
|
@ -272,6 +292,7 @@ def get_entities(onewirehub: OneWireHub, config):
|
||||||
conf_type = config[CONF_TYPE]
|
conf_type = config[CONF_TYPE]
|
||||||
# We have an owserver on a remote(or local) host/port
|
# We have an owserver on a remote(or local) host/port
|
||||||
if conf_type == CONF_TYPE_OWSERVER:
|
if conf_type == CONF_TYPE_OWSERVER:
|
||||||
|
assert onewirehub.owproxy
|
||||||
for device in onewirehub.devices:
|
for device in onewirehub.devices:
|
||||||
family = device["family"]
|
family = device["family"]
|
||||||
device_type = device["type"]
|
device_type = device["type"]
|
||||||
|
@ -292,7 +313,7 @@ def get_entities(onewirehub: OneWireHub, config):
|
||||||
device_id,
|
device_id,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
device_info = {
|
device_info: DeviceInfo = {
|
||||||
"identifiers": {(DOMAIN, device_id)},
|
"identifiers": {(DOMAIN, device_id)},
|
||||||
"manufacturer": "Maxim Integrated",
|
"manufacturer": "Maxim Integrated",
|
||||||
"model": device_type,
|
"model": device_type,
|
||||||
|
@ -384,9 +405,23 @@ class OneWireProxySensor(OneWireProxyEntity, OneWireSensor):
|
||||||
class OneWireDirectSensor(OneWireSensor):
|
class OneWireDirectSensor(OneWireSensor):
|
||||||
"""Implementation of a 1-Wire sensor directly connected to RPI GPIO."""
|
"""Implementation of a 1-Wire sensor directly connected to RPI GPIO."""
|
||||||
|
|
||||||
def __init__(self, name, device_file, device_info, owsensor):
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
device_file: str,
|
||||||
|
device_info: DeviceInfo,
|
||||||
|
owsensor: OneWireInterface,
|
||||||
|
) -> None:
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
super().__init__(name, device_file, "temperature", "Temperature", device_info)
|
super().__init__(
|
||||||
|
name,
|
||||||
|
device_file,
|
||||||
|
"temperature",
|
||||||
|
"Temperature",
|
||||||
|
device_info,
|
||||||
|
False,
|
||||||
|
device_file,
|
||||||
|
)
|
||||||
self._owsensor = owsensor
|
self._owsensor = owsensor
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -394,7 +429,7 @@ class OneWireDirectSensor(OneWireSensor):
|
||||||
"""Return the state of the entity."""
|
"""Return the state of the entity."""
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
async def get_temperature(self):
|
async def get_temperature(self) -> float:
|
||||||
"""Get the latest data from the device."""
|
"""Get the latest data from the device."""
|
||||||
attempts = 1
|
attempts = 1
|
||||||
while True:
|
while True:
|
||||||
|
@ -414,16 +449,15 @@ class OneWireDirectSensor(OneWireSensor):
|
||||||
if attempts > 10:
|
if attempts > 10:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self) -> None:
|
||||||
"""Get the latest data from the device."""
|
"""Get the latest data from the device."""
|
||||||
value = None
|
|
||||||
try:
|
try:
|
||||||
self._value_raw = await self.get_temperature()
|
self._value_raw = await self.get_temperature()
|
||||||
value = round(float(self._value_raw), 1)
|
self._state = round(self._value_raw, 1)
|
||||||
except (
|
except (
|
||||||
FileNotFoundError,
|
FileNotFoundError,
|
||||||
InvalidCRCException,
|
InvalidCRCException,
|
||||||
UnsupportResponseException,
|
UnsupportResponseException,
|
||||||
) as ex:
|
) as ex:
|
||||||
_LOGGER.warning("Cannot read from sensor %s: %s", self._device_file, ex)
|
_LOGGER.warning("Cannot read from sensor %s: %s", self._device_file, ex)
|
||||||
self._state = value
|
self._state = None
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
"""Support for 1-Wire environment switches."""
|
"""Support for 1-Wire environment switches."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity
|
from homeassistant.components.switch import SwitchEntity
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_TYPE
|
from homeassistant.const import CONF_TYPE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import CONF_TYPE_OWSERVER, DOMAIN, SWITCH_TYPE_LATCH, SWITCH_TYPE_PIO
|
from .const import CONF_TYPE_OWSERVER, DOMAIN, SWITCH_TYPE_LATCH, SWITCH_TYPE_PIO
|
||||||
from .onewire_entities import OneWireProxyEntity
|
from .model import DeviceComponentDescription
|
||||||
|
from .onewire_entities import OneWireBaseEntity, OneWireProxyEntity
|
||||||
from .onewirehub import OneWireHub
|
from .onewirehub import OneWireHub
|
||||||
|
|
||||||
DEVICE_SWITCHES = {
|
DEVICE_SWITCHES: dict[str, list[DeviceComponentDescription]] = {
|
||||||
# Family : { owfs path }
|
# Family : { owfs path }
|
||||||
"12": [
|
"12": [
|
||||||
{
|
{
|
||||||
|
@ -140,7 +148,11 @@ DEVICE_SWITCHES = {
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
"""Set up 1-Wire platform."""
|
"""Set up 1-Wire platform."""
|
||||||
# Only OWServer implementation works with switches
|
# Only OWServer implementation works with switches
|
||||||
if config_entry.data[CONF_TYPE] == CONF_TYPE_OWSERVER:
|
if config_entry.data[CONF_TYPE] == CONF_TYPE_OWSERVER:
|
||||||
|
@ -150,9 +162,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
|
|
||||||
def get_entities(onewirehub: OneWireHub):
|
def get_entities(onewirehub: OneWireHub) -> list[OneWireBaseEntity]:
|
||||||
"""Get a list of entities."""
|
"""Get a list of entities."""
|
||||||
entities = []
|
if not onewirehub.devices:
|
||||||
|
return []
|
||||||
|
|
||||||
|
entities: list[OneWireBaseEntity] = []
|
||||||
|
|
||||||
for device in onewirehub.devices:
|
for device in onewirehub.devices:
|
||||||
family = device["family"]
|
family = device["family"]
|
||||||
|
@ -162,7 +177,7 @@ def get_entities(onewirehub: OneWireHub):
|
||||||
if family not in DEVICE_SWITCHES:
|
if family not in DEVICE_SWITCHES:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
device_info = {
|
device_info: DeviceInfo = {
|
||||||
"identifiers": {(DOMAIN, device_id)},
|
"identifiers": {(DOMAIN, device_id)},
|
||||||
"manufacturer": "Maxim Integrated",
|
"manufacturer": "Maxim Integrated",
|
||||||
"model": device_type,
|
"model": device_type,
|
||||||
|
@ -190,14 +205,14 @@ class OneWireProxySwitch(OneWireProxyEntity, SwitchEntity):
|
||||||
"""Implementation of a 1-Wire switch."""
|
"""Implementation of a 1-Wire switch."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self) -> bool:
|
||||||
"""Return true if sensor is on."""
|
"""Return true if sensor is on."""
|
||||||
return self._state
|
return bool(self._state)
|
||||||
|
|
||||||
def turn_on(self, **kwargs) -> None:
|
def turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity on."""
|
"""Turn the entity on."""
|
||||||
self._write_value_ownet(b"1")
|
self._write_value_ownet(b"1")
|
||||||
|
|
||||||
def turn_off(self, **kwargs) -> None:
|
def turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity off."""
|
"""Turn the entity off."""
|
||||||
self._write_value_ownet(b"0")
|
self._write_value_ownet(b"0")
|
||||||
|
|
14
mypy.ini
14
mypy.ini
|
@ -352,6 +352,17 @@ no_implicit_optional = true
|
||||||
warn_return_any = true
|
warn_return_any = true
|
||||||
warn_unreachable = true
|
warn_unreachable = true
|
||||||
|
|
||||||
|
[mypy-homeassistant.components.onewire.*]
|
||||||
|
check_untyped_defs = true
|
||||||
|
disallow_incomplete_defs = true
|
||||||
|
disallow_subclassing_any = true
|
||||||
|
disallow_untyped_calls = true
|
||||||
|
disallow_untyped_decorators = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
|
no_implicit_optional = true
|
||||||
|
warn_return_any = true
|
||||||
|
warn_unreachable = true
|
||||||
|
|
||||||
[mypy-homeassistant.components.persistent_notification.*]
|
[mypy-homeassistant.components.persistent_notification.*]
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
disallow_incomplete_defs = true
|
disallow_incomplete_defs = true
|
||||||
|
@ -1008,9 +1019,6 @@ ignore_errors = true
|
||||||
[mypy-homeassistant.components.ondilo_ico.*]
|
[mypy-homeassistant.components.ondilo_ico.*]
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
|
|
||||||
[mypy-homeassistant.components.onewire.*]
|
|
||||||
ignore_errors = true
|
|
||||||
|
|
||||||
[mypy-homeassistant.components.onvif.*]
|
[mypy-homeassistant.components.onvif.*]
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,6 @@ IGNORED_MODULES: Final[list[str]] = [
|
||||||
"homeassistant.components.omnilogic.*",
|
"homeassistant.components.omnilogic.*",
|
||||||
"homeassistant.components.onboarding.*",
|
"homeassistant.components.onboarding.*",
|
||||||
"homeassistant.components.ondilo_ico.*",
|
"homeassistant.components.ondilo_ico.*",
|
||||||
"homeassistant.components.onewire.*",
|
|
||||||
"homeassistant.components.onvif.*",
|
"homeassistant.components.onvif.*",
|
||||||
"homeassistant.components.ovo_energy.*",
|
"homeassistant.components.ovo_energy.*",
|
||||||
"homeassistant.components.ozw.*",
|
"homeassistant.components.ozw.*",
|
||||||
|
|
|
@ -884,7 +884,7 @@ MOCK_SYSBUS_DEVICES = {
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.42_111111111112_temperature",
|
"entity_id": "sensor.42_111111111112_temperature",
|
||||||
"unique_id": "/sys/bus/w1/devices/42-111111111112/w1_slave",
|
"unique_id": "/sys/bus/w1/devices/42-111111111112/w1_slave",
|
||||||
"injected_value": [UnsupportResponseException] * 9 + ["27.993"],
|
"injected_value": [UnsupportResponseException] * 9 + [27.993],
|
||||||
"result": "28.0",
|
"result": "28.0",
|
||||||
"unit": TEMP_CELSIUS,
|
"unit": TEMP_CELSIUS,
|
||||||
"class": DEVICE_CLASS_TEMPERATURE,
|
"class": DEVICE_CLASS_TEMPERATURE,
|
||||||
|
@ -902,7 +902,7 @@ MOCK_SYSBUS_DEVICES = {
|
||||||
{
|
{
|
||||||
"entity_id": "sensor.42_111111111113_temperature",
|
"entity_id": "sensor.42_111111111113_temperature",
|
||||||
"unique_id": "/sys/bus/w1/devices/42-111111111113/w1_slave",
|
"unique_id": "/sys/bus/w1/devices/42-111111111113/w1_slave",
|
||||||
"injected_value": [UnsupportResponseException] * 10 + ["27.993"],
|
"injected_value": [UnsupportResponseException] * 10 + [27.993],
|
||||||
"result": "unknown",
|
"result": "unknown",
|
||||||
"unit": TEMP_CELSIUS,
|
"unit": TEMP_CELSIUS,
|
||||||
"class": DEVICE_CLASS_TEMPERATURE,
|
"class": DEVICE_CLASS_TEMPERATURE,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue