Merge remote-tracking branch 'upstream/dev' into conf_state_class
commit
1d616e920d
16
.coveragerc
16
.coveragerc
|
@ -36,6 +36,9 @@ omit =
|
|||
homeassistant/components/agent_dvr/helpers.py
|
||||
homeassistant/components/airnow/__init__.py
|
||||
homeassistant/components/airnow/sensor.py
|
||||
homeassistant/components/airtouch4/__init__.py
|
||||
homeassistant/components/airtouch4/climate.py
|
||||
homeassistant/components/airtouch4/const.py
|
||||
homeassistant/components/airvisual/__init__.py
|
||||
homeassistant/components/airvisual/sensor.py
|
||||
homeassistant/components/aladdin_connect/*
|
||||
|
@ -375,6 +378,7 @@ omit =
|
|||
homeassistant/components/google/*
|
||||
homeassistant/components/google_cloud/tts.py
|
||||
homeassistant/components/google_maps/device_tracker.py
|
||||
homeassistant/components/google_pubsub/__init__.py
|
||||
homeassistant/components/google_travel_time/__init__.py
|
||||
homeassistant/components/google_travel_time/helpers.py
|
||||
homeassistant/components/google_travel_time/sensor.py
|
||||
|
@ -666,17 +670,19 @@ omit =
|
|||
homeassistant/components/mysensors/helpers.py
|
||||
homeassistant/components/mysensors/light.py
|
||||
homeassistant/components/mysensors/notify.py
|
||||
homeassistant/components/mysensors/sensor.py
|
||||
homeassistant/components/mysensors/switch.py
|
||||
homeassistant/components/mystrom/binary_sensor.py
|
||||
homeassistant/components/mystrom/light.py
|
||||
homeassistant/components/mystrom/switch.py
|
||||
homeassistant/components/myq/__init__.py
|
||||
homeassistant/components/myq/cover.py
|
||||
homeassistant/components/myq/light.py
|
||||
homeassistant/components/nad/media_player.py
|
||||
homeassistant/components/nanoleaf/light.py
|
||||
homeassistant/components/neato/__init__.py
|
||||
homeassistant/components/neato/api.py
|
||||
homeassistant/components/neato/camera.py
|
||||
homeassistant/components/neato/hub.py
|
||||
homeassistant/components/neato/sensor.py
|
||||
homeassistant/components/neato/switch.py
|
||||
homeassistant/components/neato/vacuum.py
|
||||
|
@ -695,7 +701,8 @@ omit =
|
|||
homeassistant/components/niko_home_control/light.py
|
||||
homeassistant/components/nilu/air_quality.py
|
||||
homeassistant/components/nissan_leaf/*
|
||||
homeassistant/components/nmap_tracker/*
|
||||
homeassistant/components/nmap_tracker/__init__.py
|
||||
homeassistant/components/nmap_tracker/device_tracker.py
|
||||
homeassistant/components/nmbs/sensor.py
|
||||
homeassistant/components/notion/__init__.py
|
||||
homeassistant/components/notion/binary_sensor.py
|
||||
|
@ -1114,10 +1121,6 @@ omit =
|
|||
homeassistant/components/upcloud/switch.py
|
||||
homeassistant/components/upnp/*
|
||||
homeassistant/components/upc_connect/*
|
||||
homeassistant/components/uptimerobot/__init__.py
|
||||
homeassistant/components/uptimerobot/binary_sensor.py
|
||||
homeassistant/components/uptimerobot/const.py
|
||||
homeassistant/components/uptimerobot/entity.py
|
||||
homeassistant/components/uscis/sensor.py
|
||||
homeassistant/components/vallox/*
|
||||
homeassistant/components/vasttrafik/sensor.py
|
||||
|
@ -1201,6 +1204,7 @@ omit =
|
|||
homeassistant/components/xiaomi_miio/__init__.py
|
||||
homeassistant/components/xiaomi_miio/air_quality.py
|
||||
homeassistant/components/xiaomi_miio/alarm_control_panel.py
|
||||
homeassistant/components/xiaomi_miio/binary_sensor.py
|
||||
homeassistant/components/xiaomi_miio/device.py
|
||||
homeassistant/components/xiaomi_miio/device_tracker.py
|
||||
homeassistant/components/xiaomi_miio/fan.py
|
||||
|
|
|
@ -71,6 +71,7 @@ If the code communicates with devices, web services, or third-party tools:
|
|||
Updated and included derived files by running: `python3 -m script.hassfest`.
|
||||
- [ ] New or updated dependencies have been added to `requirements_all.txt`.
|
||||
Updated by running `python3 -m script.gen_requirements_all`.
|
||||
- [ ] For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.
|
||||
- [ ] Untested files have been added to `.coveragerc`.
|
||||
|
||||
The integration reached or maintains the following [Integration Quality Scale][quality-scale]:
|
||||
|
|
|
@ -248,11 +248,12 @@ jobs:
|
|||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install VCN tools
|
||||
uses: home-assistant/actions/helpers/vcn@master
|
||||
|
||||
- name: Build Meta Image
|
||||
shell: bash
|
||||
run: |
|
||||
bash <(curl https://getvcn.codenotary.com -L)
|
||||
|
||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
|
||||
function create_manifest() {
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v2.1.1
|
||||
- uses: dessant/lock-threads@v2.1.2
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: "30"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
repos:
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.23.0
|
||||
rev: v2.23.3
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py38-plus]
|
||||
|
@ -45,7 +45,7 @@ repos:
|
|||
- --configfile=tests/bandit.yaml
|
||||
files: ^(homeassistant|script|tests)/.+\.py$
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.8.0
|
||||
rev: 5.9.3
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
|
|
|
@ -63,6 +63,7 @@ homeassistant.components.mailbox.*
|
|||
homeassistant.components.media_player.*
|
||||
homeassistant.components.mysensors.*
|
||||
homeassistant.components.nam.*
|
||||
homeassistant.components.neato.*
|
||||
homeassistant.components.nest.*
|
||||
homeassistant.components.netatmo.*
|
||||
homeassistant.components.network.*
|
||||
|
|
12
CODEOWNERS
12
CODEOWNERS
|
@ -29,6 +29,7 @@ homeassistant/components/aemet/* @noltari
|
|||
homeassistant/components/agent_dvr/* @ispysoftware
|
||||
homeassistant/components/airly/* @bieniu
|
||||
homeassistant/components/airnow/* @asymworks
|
||||
homeassistant/components/airtouch4/* @LonePurpleWolf
|
||||
homeassistant/components/airvisual/* @bachya
|
||||
homeassistant/components/alarmdecoder/* @ajschmidt8
|
||||
homeassistant/components/alexa/* @home-assistant/cloud @ochlocracy
|
||||
|
@ -187,6 +188,7 @@ homeassistant/components/geo_rss_events/* @exxamalte
|
|||
homeassistant/components/geonetnz_quakes/* @exxamalte
|
||||
homeassistant/components/geonetnz_volcano/* @exxamalte
|
||||
homeassistant/components/gios/* @bieniu
|
||||
homeassistant/components/github/* @timmo001
|
||||
homeassistant/components/gitter/* @fabaff
|
||||
homeassistant/components/glances/* @fabaff @engrbm87
|
||||
homeassistant/components/goalzero/* @tkdrob
|
||||
|
@ -320,7 +322,7 @@ homeassistant/components/msteams/* @peroyvind
|
|||
homeassistant/components/mullvad/* @meichthys
|
||||
homeassistant/components/mutesync/* @currentoor
|
||||
homeassistant/components/my/* @home-assistant/core
|
||||
homeassistant/components/myq/* @bdraco
|
||||
homeassistant/components/myq/* @bdraco @ehendrix23
|
||||
homeassistant/components/mysensors/* @MartinHjelmare @functionpointer
|
||||
homeassistant/components/mystrom/* @fabaff
|
||||
homeassistant/components/nam/* @bieniu
|
||||
|
@ -338,6 +340,7 @@ homeassistant/components/nfandroidtv/* @tkdrob
|
|||
homeassistant/components/nightscout/* @marciogranzotto
|
||||
homeassistant/components/nilu/* @hfurubotten
|
||||
homeassistant/components/nissan_leaf/* @filcole
|
||||
homeassistant/components/nmap_tracker/* @bdraco
|
||||
homeassistant/components/nmbs/* @thibmaek
|
||||
homeassistant/components/no_ip/* @fabaff
|
||||
homeassistant/components/noaa_tides/* @jdelaney72
|
||||
|
@ -503,7 +506,7 @@ homeassistant/components/synology_dsm/* @hacf-fr @Quentame @mib1185
|
|||
homeassistant/components/synology_srm/* @aerialls
|
||||
homeassistant/components/syslog/* @fabaff
|
||||
homeassistant/components/system_bridge/* @timmo001
|
||||
homeassistant/components/tado/* @michaelarnauts @bdraco @noltari
|
||||
homeassistant/components/tado/* @michaelarnauts @noltari
|
||||
homeassistant/components/tag/* @balloob @dmulcahey
|
||||
homeassistant/components/tahoma/* @philklei
|
||||
homeassistant/components/tankerkoenig/* @guillempages
|
||||
|
@ -527,6 +530,7 @@ homeassistant/components/tplink/* @rytilahti @thegardenmonkey
|
|||
homeassistant/components/traccar/* @ludeeus
|
||||
homeassistant/components/trace/* @home-assistant/core
|
||||
homeassistant/components/tractive/* @Danielhiversen @zhulik
|
||||
homeassistant/components/tradfri/* @janiversen
|
||||
homeassistant/components/trafikverket_train/* @endor-force
|
||||
homeassistant/components/trafikverket_weatherstation/* @endor-force
|
||||
homeassistant/components/transmission/* @engrbm87 @JPHutchins
|
||||
|
@ -541,7 +545,7 @@ homeassistant/components/upb/* @gwww
|
|||
homeassistant/components/upc_connect/* @pvizeli @fabaff
|
||||
homeassistant/components/upcloud/* @scop
|
||||
homeassistant/components/updater/* @home-assistant/core
|
||||
homeassistant/components/upnp/* @StevenLooman
|
||||
homeassistant/components/upnp/* @StevenLooman @ehendrix23
|
||||
homeassistant/components/uptimerobot/* @ludeeus
|
||||
homeassistant/components/usgs_earthquakes_feed/* @exxamalte
|
||||
homeassistant/components/utility_meter/* @dgomes
|
||||
|
@ -584,7 +588,7 @@ homeassistant/components/xmpp/* @fabaff @flowolf
|
|||
homeassistant/components/yale_smart_alarm/* @gjohansson-ST
|
||||
homeassistant/components/yamaha_musiccast/* @vigonotion @micha91
|
||||
homeassistant/components/yandex_transport/* @rishatik92 @devbis
|
||||
homeassistant/components/yeelight/* @rytilahti @zewelor @shenxn
|
||||
homeassistant/components/yeelight/* @rytilahti @zewelor @shenxn @starkillerOG
|
||||
homeassistant/components/yeelightsunflower/* @lindsaymarkward
|
||||
homeassistant/components/yi/* @bachya
|
||||
homeassistant/components/youless/* @gjong
|
||||
|
|
|
@ -17,6 +17,8 @@ from .const import GROUP_ID_ADMIN, GROUP_ID_READ_ONLY, GROUP_ID_USER
|
|||
from .permissions import PermissionLookup, system_policies
|
||||
from .permissions.types import PolicyType
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
STORAGE_VERSION = 1
|
||||
STORAGE_KEY = "auth"
|
||||
GROUP_NAME_ADMIN = "Administrators"
|
||||
|
@ -491,7 +493,7 @@ class AuthStore:
|
|||
self._store.async_delay_save(self._data_to_save, 1)
|
||||
|
||||
@callback
|
||||
def _data_to_save(self) -> dict:
|
||||
def _data_to_save(self) -> dict[str, list[dict[str, Any]]]:
|
||||
"""Return the data to store."""
|
||||
assert self._users is not None
|
||||
assert self._groups is not None
|
||||
|
|
|
@ -22,6 +22,8 @@ from ..auth_store import AuthStore
|
|||
from ..const import MFA_SESSION_EXPIRATION
|
||||
from ..models import Credentials, RefreshToken, User, UserMeta
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
DATA_REQS = "auth_prov_reqs_processed"
|
||||
|
||||
|
@ -96,7 +98,7 @@ class AuthProvider:
|
|||
|
||||
# Implement by extending class
|
||||
|
||||
async def async_login_flow(self, context: dict | None) -> LoginFlow:
|
||||
async def async_login_flow(self, context: dict[str, Any] | None) -> LoginFlow:
|
||||
"""Return the data flow for logging in with auth provider.
|
||||
|
||||
Auth provider should extend LoginFlow and return an instance.
|
||||
|
|
|
@ -17,6 +17,8 @@ from homeassistant.exceptions import HomeAssistantError
|
|||
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
|
||||
from ..models import Credentials, UserMeta
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
CONF_ARGS = "args"
|
||||
CONF_META = "meta"
|
||||
|
||||
|
@ -56,7 +58,7 @@ class CommandLineAuthProvider(AuthProvider):
|
|||
super().__init__(*args, **kwargs)
|
||||
self._user_meta: dict[str, dict[str, Any]] = {}
|
||||
|
||||
async def async_login_flow(self, context: dict | None) -> LoginFlow:
|
||||
async def async_login_flow(self, context: dict[str, Any] | None) -> LoginFlow:
|
||||
"""Return a flow to login."""
|
||||
return CommandLineLoginFlow(self)
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ from homeassistant.exceptions import HomeAssistantError
|
|||
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
|
||||
from ..models import Credentials, UserMeta
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
STORAGE_VERSION = 1
|
||||
STORAGE_KEY = "auth_provider.homeassistant"
|
||||
|
||||
|
@ -235,7 +237,7 @@ class HassAuthProvider(AuthProvider):
|
|||
await data.async_load()
|
||||
self.data = data
|
||||
|
||||
async def async_login_flow(self, context: dict | None) -> LoginFlow:
|
||||
async def async_login_flow(self, context: dict[str, Any] | None) -> LoginFlow:
|
||||
"""Return a flow to login."""
|
||||
return HassLoginFlow(self)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||
from collections import OrderedDict
|
||||
from collections.abc import Mapping
|
||||
import hmac
|
||||
from typing import cast
|
||||
from typing import Any, cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
|
@ -15,6 +15,8 @@ from homeassistant.exceptions import HomeAssistantError
|
|||
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
|
||||
from ..models import Credentials, UserMeta
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
USER_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required("username"): str,
|
||||
|
@ -37,7 +39,7 @@ class InvalidAuthError(HomeAssistantError):
|
|||
class ExampleAuthProvider(AuthProvider):
|
||||
"""Example auth provider based on hardcoded usernames and passwords."""
|
||||
|
||||
async def async_login_flow(self, context: dict | None) -> LoginFlow:
|
||||
async def async_login_flow(self, context: dict[str, Any] | None) -> LoginFlow:
|
||||
"""Return a flow to login."""
|
||||
return ExampleLoginFlow(self)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from __future__ import annotations
|
|||
|
||||
from collections.abc import Mapping
|
||||
import hmac
|
||||
from typing import cast
|
||||
from typing import Any, cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
|
@ -19,6 +19,8 @@ import homeassistant.helpers.config_validation as cv
|
|||
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
|
||||
from ..models import Credentials, UserMeta
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
AUTH_PROVIDER_TYPE = "legacy_api_password"
|
||||
CONF_API_PASSWORD = "api_password"
|
||||
|
||||
|
@ -44,7 +46,7 @@ class LegacyApiPasswordAuthProvider(AuthProvider):
|
|||
"""Return api_password."""
|
||||
return str(self.config[CONF_API_PASSWORD])
|
||||
|
||||
async def async_login_flow(self, context: dict | None) -> LoginFlow:
|
||||
async def async_login_flow(self, context: dict[str, Any] | None) -> LoginFlow:
|
||||
"""Return a flow to login."""
|
||||
return LegacyLoginFlow(self)
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
|
|||
from .. import InvalidAuthError
|
||||
from ..models import Credentials, RefreshToken, UserMeta
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
IPAddress = Union[IPv4Address, IPv6Address]
|
||||
IPNetwork = Union[IPv4Network, IPv6Network]
|
||||
|
||||
|
@ -97,7 +99,7 @@ class TrustedNetworksAuthProvider(AuthProvider):
|
|||
"""Trusted Networks auth provider does not support MFA."""
|
||||
return False
|
||||
|
||||
async def async_login_flow(self, context: dict | None) -> LoginFlow:
|
||||
async def async_login_flow(self, context: dict[str, Any] | None) -> LoginFlow:
|
||||
"""Return a flow to login."""
|
||||
assert context is not None
|
||||
ip_addr = cast(IPAddress, context.get("ip_address"))
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"""Support for Abode Security System cameras."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
import abodepy.helpers.constants as CONST
|
||||
|
@ -73,7 +75,9 @@ class AbodeCamera(AbodeDevice, Camera):
|
|||
else:
|
||||
self._response = None
|
||||
|
||||
def camera_image(self):
|
||||
def camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Get a camera image."""
|
||||
self.refresh_image()
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
"""Support for Abode Security System sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
import abodepy.helpers.constants as CONST
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.const import (
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
|
@ -11,12 +13,23 @@ from homeassistant.const import (
|
|||
from . import AbodeDevice
|
||||
from .const import DOMAIN
|
||||
|
||||
# Sensor types: Name, icon
|
||||
SENSOR_TYPES = {
|
||||
CONST.TEMP_STATUS_KEY: ["Temperature", DEVICE_CLASS_TEMPERATURE],
|
||||
CONST.HUMI_STATUS_KEY: ["Humidity", DEVICE_CLASS_HUMIDITY],
|
||||
CONST.LUX_STATUS_KEY: ["Lux", DEVICE_CLASS_ILLUMINANCE],
|
||||
}
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=CONST.TEMP_STATUS_KEY,
|
||||
name="Temperature",
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=CONST.HUMI_STATUS_KEY,
|
||||
name="Humidity",
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=CONST.LUX_STATUS_KEY,
|
||||
name="Lux",
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
|
@ -26,10 +39,14 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
entities = []
|
||||
|
||||
for device in data.abode.get_devices(generic_type=CONST.TYPE_SENSOR):
|
||||
for sensor_type in SENSOR_TYPES:
|
||||
if sensor_type not in device.get_value(CONST.STATUSES_KEY):
|
||||
continue
|
||||
entities.append(AbodeSensor(data, device, sensor_type))
|
||||
conditions = device.get_value(CONST.STATUSES_KEY)
|
||||
entities.extend(
|
||||
[
|
||||
AbodeSensor(data, device, description)
|
||||
for description in SENSOR_TYPES
|
||||
if description.key in conditions
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
@ -37,26 +54,25 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
class AbodeSensor(AbodeDevice, SensorEntity):
|
||||
"""A sensor implementation for Abode devices."""
|
||||
|
||||
def __init__(self, data, device, sensor_type):
|
||||
def __init__(self, data, device, description: SensorEntityDescription):
|
||||
"""Initialize a sensor for an Abode device."""
|
||||
super().__init__(data, device)
|
||||
self._sensor_type = sensor_type
|
||||
self._attr_name = f"{device.name} {SENSOR_TYPES[sensor_type][0]}"
|
||||
self._attr_device_class = SENSOR_TYPES[self._sensor_type][1]
|
||||
self._attr_unique_id = f"{device.device_uuid}-{sensor_type}"
|
||||
if self._sensor_type == CONST.TEMP_STATUS_KEY:
|
||||
self._attr_unit_of_measurement = device.temp_unit
|
||||
elif self._sensor_type == CONST.HUMI_STATUS_KEY:
|
||||
self._attr_unit_of_measurement = device.humidity_unit
|
||||
elif self._sensor_type == CONST.LUX_STATUS_KEY:
|
||||
self._attr_unit_of_measurement = device.lux_unit
|
||||
self.entity_description = description
|
||||
self._attr_name = f"{device.name} {description.name}"
|
||||
self._attr_unique_id = f"{device.device_uuid}-{description.key}"
|
||||
if description.key == CONST.TEMP_STATUS_KEY:
|
||||
self._attr_native_unit_of_measurement = device.temp_unit
|
||||
elif description.key == CONST.HUMI_STATUS_KEY:
|
||||
self._attr_native_unit_of_measurement = device.humidity_unit
|
||||
elif description.key == CONST.LUX_STATUS_KEY:
|
||||
self._attr_native_unit_of_measurement = device.lux_unit
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state of the sensor."""
|
||||
if self._sensor_type == CONST.TEMP_STATUS_KEY:
|
||||
if self.entity_description.key == CONST.TEMP_STATUS_KEY:
|
||||
return self._device.temp
|
||||
if self._sensor_type == CONST.HUMI_STATUS_KEY:
|
||||
if self.entity_description.key == CONST.HUMI_STATUS_KEY:
|
||||
return self._device.humidity
|
||||
if self._sensor_type == CONST.LUX_STATUS_KEY:
|
||||
if self.entity_description.key == CONST.LUX_STATUS_KEY:
|
||||
return self._device.lux
|
||||
|
|
|
@ -88,10 +88,10 @@ class AccuWeatherSensor(CoordinatorEntity, SensorEntity):
|
|||
)
|
||||
if coordinator.is_metric:
|
||||
self._unit_system = API_METRIC
|
||||
self._attr_unit_of_measurement = description.unit_metric
|
||||
self._attr_native_unit_of_measurement = description.unit_metric
|
||||
else:
|
||||
self._unit_system = API_IMPERIAL
|
||||
self._attr_unit_of_measurement = description.unit_imperial
|
||||
self._attr_native_unit_of_measurement = description.unit_imperial
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, coordinator.location_key)},
|
||||
"name": NAME,
|
||||
|
@ -101,7 +101,7 @@ class AccuWeatherSensor(CoordinatorEntity, SensorEntity):
|
|||
self.forecast_day = forecast_day
|
||||
|
||||
@property
|
||||
def state(self) -> StateType:
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state."""
|
||||
if self.forecast_day is not None:
|
||||
if self.entity_description.device_class == DEVICE_CLASS_TEMPERATURE:
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
},
|
||||
"error": {
|
||||
"cannot_connect": "Sikertelen csatlakoz\u00e1s",
|
||||
"invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs"
|
||||
"invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs",
|
||||
"requests_exceeded": "T\u00fall\u00e9pt\u00e9k az Accuweather API-hoz beny\u00fajtott k\u00e9relmek megengedett sz\u00e1m\u00e1t. Meg kell v\u00e1rnia vagy m\u00f3dos\u00edtania kell az API-kulcsot."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
|
@ -15,6 +16,7 @@
|
|||
"longitude": "Hossz\u00fas\u00e1g",
|
||||
"name": "N\u00e9v"
|
||||
},
|
||||
"description": "Ha seg\u00edts\u00e9gre van sz\u00fcks\u00e9ge a konfigur\u00e1l\u00e1shoz, n\u00e9zze meg itt: https://www.home-assistant.io/integrations/accuweather/ \n\nEgyes \u00e9rz\u00e9kel\u0151k alap\u00e9rtelmez\u00e9s szerint nincsenek enged\u00e9lyezve. Az integr\u00e1ci\u00f3s konfigur\u00e1ci\u00f3 ut\u00e1n enged\u00e9lyezheti \u0151ket az entit\u00e1s-nyilv\u00e1ntart\u00e1sban.\nAz id\u0151j\u00e1r\u00e1s-el\u0151rejelz\u00e9s alap\u00e9rtelmez\u00e9s szerint nincs enged\u00e9lyezve. Ezt az integr\u00e1ci\u00f3s be\u00e1ll\u00edt\u00e1sokban enged\u00e9lyezheti.",
|
||||
"title": "AccuWeather"
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +24,10 @@
|
|||
"options": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"forecast": "Id\u0151j\u00e1r\u00e1s el\u0151rejelz\u00e9s"
|
||||
},
|
||||
"description": "Az AccuWeather API kulcs ingyenes verzi\u00f3j\u00e1nak korl\u00e1tai miatt, amikor enged\u00e9lyezi az id\u0151j\u00e1r\u00e1s -el\u0151rejelz\u00e9st, az adatfriss\u00edt\u00e9seket 40 percenk\u00e9nt 80 percenk\u00e9nt hajtj\u00e1k v\u00e9gre.",
|
||||
"title": "AccuWeather be\u00e1ll\u00edt\u00e1sok"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class AcmedaBattery(AcmedaBase, SensorEntity):
|
|||
"""Representation of a Acmeda cover device."""
|
||||
|
||||
device_class = DEVICE_CLASS_BATTERY
|
||||
unit_of_measurement = PERCENTAGE
|
||||
_attr_native_unit_of_measurement = PERCENTAGE
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -42,6 +42,6 @@ class AcmedaBattery(AcmedaBase, SensorEntity):
|
|||
return f"{super().name} Battery"
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
return self.roller.battery
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
"config": {
|
||||
"abort": {
|
||||
"no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "Gazdag\u00e9p azonos\u00edt\u00f3"
|
||||
},
|
||||
"title": "V\u00e1lassza ki a hozz\u00e1adni k\u00edv\u00e1nt hubot"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,20 +49,19 @@ async def async_setup_entry(
|
|||
class AdaxDevice(ClimateEntity):
|
||||
"""Representation of a heater."""
|
||||
|
||||
_attr_hvac_modes = [HVAC_MODE_HEAT, HVAC_MODE_OFF]
|
||||
_attr_max_temp = 35
|
||||
_attr_min_temp = 5
|
||||
_attr_supported_features = SUPPORT_TARGET_TEMPERATURE
|
||||
_attr_target_temperature_step = PRECISION_WHOLE
|
||||
_attr_temperature_unit = TEMP_CELSIUS
|
||||
|
||||
def __init__(self, heater_data: dict[str, Any], adax_data_handler: Adax) -> None:
|
||||
"""Initialize the heater."""
|
||||
self._heater_data = heater_data
|
||||
self._adax_data_handler = adax_data_handler
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Return the list of supported features."""
|
||||
return SUPPORT_TARGET_TEMPERATURE
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique ID."""
|
||||
return f"{self._heater_data['homeId']}_{self._heater_data['id']}"
|
||||
self._attr_unique_id = f"{heater_data['homeId']}_{heater_data['id']}"
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
|
@ -83,11 +82,6 @@ class AdaxDevice(ClimateEntity):
|
|||
return "mdi:radiator"
|
||||
return "mdi:radiator-off"
|
||||
|
||||
@property
|
||||
def hvac_modes(self) -> list[str]:
|
||||
"""Return the list of available hvac operation modes."""
|
||||
return [HVAC_MODE_HEAT, HVAC_MODE_OFF]
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
|
||||
"""Set hvac mode."""
|
||||
if hvac_mode == HVAC_MODE_HEAT:
|
||||
|
@ -105,21 +99,6 @@ class AdaxDevice(ClimateEntity):
|
|||
return
|
||||
await self._adax_data_handler.update()
|
||||
|
||||
@property
|
||||
def temperature_unit(self) -> str:
|
||||
"""Return the unit of measurement which this device uses."""
|
||||
return TEMP_CELSIUS
|
||||
|
||||
@property
|
||||
def min_temp(self) -> int:
|
||||
"""Return the minimum temperature."""
|
||||
return 5
|
||||
|
||||
@property
|
||||
def max_temp(self) -> int:
|
||||
"""Return the maximum temperature."""
|
||||
return 35
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
"""Return the current temperature."""
|
||||
|
@ -130,11 +109,6 @@ class AdaxDevice(ClimateEntity):
|
|||
"""Return the temperature we try to reach."""
|
||||
return self._heater_data.get("targetTemperature")
|
||||
|
||||
@property
|
||||
def target_temperature_step(self) -> int:
|
||||
"""Return the supported step of target temperature."""
|
||||
return PRECISION_WHOLE
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/adax",
|
||||
"requirements": [
|
||||
"adax==0.0.1"
|
||||
"adax==0.1.1"
|
||||
],
|
||||
"codeowners": [
|
||||
"@danielhiversen"
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"account_id": "ID \u00fa\u010dtu",
|
||||
"host": "Hostitel",
|
||||
"password": "Heslo"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"account_id": "ID de la cuenta"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nem siker\u00fclt csatlakozni",
|
||||
"invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"account_id": "Fi\u00f3k ID",
|
||||
"host": "Gazdag\u00e9p",
|
||||
"password": "Jelsz\u00f3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Enheten er allerede konfigurert"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Tilkobling mislyktes",
|
||||
"invalid_auth": "Ugyldig godkjenning"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"account_id": "Konto-ID",
|
||||
"host": "Vert",
|
||||
"password": "Passord"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"cannot_connect": "\u8fde\u63a5\u5931\u8d25"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "\u5bc6\u7801"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -82,12 +82,12 @@ class AdGuardHomeSensor(AdGuardHomeDeviceEntity, SensorEntity):
|
|||
)
|
||||
|
||||
@property
|
||||
def state(self) -> str | None:
|
||||
def native_value(self) -> str | None:
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self) -> str | None:
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
"""Return the unit this state is expressed in."""
|
||||
return self._unit_of_measurement
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van"
|
||||
"already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van",
|
||||
"existing_instance_updated": "Friss\u00edtette a megl\u00e9v\u0151 konfigur\u00e1ci\u00f3t."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
|
||||
|
@ -19,7 +20,8 @@
|
|||
"ssl": "SSL tan\u00fas\u00edtv\u00e1ny haszn\u00e1lata",
|
||||
"username": "Felhaszn\u00e1l\u00f3n\u00e9v",
|
||||
"verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se"
|
||||
}
|
||||
},
|
||||
"description": "\u00c1ll\u00edtsa be az AdGuard Home p\u00e9ld\u00e1nyt, hogy lehet\u0151v\u00e9 tegye a fel\u00fcgyeletet \u00e9s az ir\u00e1ny\u00edt\u00e1st."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u670d\u52a1\u5df2\u88ab\u914d\u7f6e",
|
||||
"existing_instance_updated": "\u66f4\u65b0\u4e86\u73b0\u6709\u914d\u7f6e\u3002"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u8fde\u63a5\u5931\u8d25"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u4e3b\u673a\u5730\u5740",
|
||||
"password": "\u5bc6\u7801",
|
||||
"username": "\u7528\u6237\u540d"
|
||||
}
|
||||
"port": "\u7aef\u53e3",
|
||||
"ssl": "\u4f7f\u7528 SSL \u8bc1\u4e66\u51ed\u8bc1",
|
||||
"username": "\u7528\u6237\u540d",
|
||||
"verify_ssl": "\u9a8c\u8bc1 SSL \u8bc1\u4e66\u51ed\u8bc1"
|
||||
},
|
||||
"description": "\u8bbe\u7f6e\u60a8\u7684 AdGuard Home \u5b9e\u4f8b\u4ee5\u5141\u8bb8\u76d1\u89c6\u548c\u63a7\u5236"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,13 +91,13 @@ class AdsCover(AdsEntity, CoverEntity):
|
|||
):
|
||||
"""Initialize AdsCover entity."""
|
||||
super().__init__(ads_hub, name, ads_var_is_closed)
|
||||
if self._ads_var is None:
|
||||
if self._attr_unique_id is None:
|
||||
if ads_var_position is not None:
|
||||
self._unique_id = ads_var_position
|
||||
self._attr_unique_id = ads_var_position
|
||||
elif ads_var_pos_set is not None:
|
||||
self._unique_id = ads_var_pos_set
|
||||
self._attr_unique_id = ads_var_pos_set
|
||||
elif ads_var_open is not None:
|
||||
self._unique_id = ads_var_open
|
||||
self._attr_unique_id = ads_var_open
|
||||
|
||||
self._state_dict[STATE_KEY_POSITION] = None
|
||||
self._ads_var_position = ads_var_position
|
||||
|
|
|
@ -50,7 +50,7 @@ class AdsSensor(AdsEntity, SensorEntity):
|
|||
def __init__(self, ads_hub, ads_var, ads_type, name, unit_of_measurement, factor):
|
||||
"""Initialize AdsSensor entity."""
|
||||
super().__init__(ads_hub, name, ads_var)
|
||||
self._attr_unit_of_measurement = unit_of_measurement
|
||||
self._attr_native_unit_of_measurement = unit_of_measurement
|
||||
self._ads_type = ads_type
|
||||
self._factor = factor
|
||||
|
||||
|
@ -64,6 +64,6 @@ class AdsSensor(AdsEntity, SensorEntity):
|
|||
)
|
||||
|
||||
@property
|
||||
def state(self) -> StateType:
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state of the device."""
|
||||
return self._state_dict[STATE_KEY_STATE]
|
||||
|
|
|
@ -15,7 +15,6 @@ from homeassistant.components.climate.const import (
|
|||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import entity_platform
|
||||
|
||||
from .const import (
|
||||
|
@ -166,19 +165,22 @@ class AdvantageAirZone(AdvantageAirClimateEntity):
|
|||
f'{self.coordinator.data["system"]["rid"]}-{ac_key}-{zone_key}'
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""When entity is added to hass."""
|
||||
self.async_on_remove(self.coordinator.async_add_listener(self._update_callback))
|
||||
|
||||
@callback
|
||||
def _update_callback(self) -> None:
|
||||
"""Load data from integration."""
|
||||
self._attr_current_temperature = self._zone["measuredTemp"]
|
||||
self._attr_target_temperature = self._zone["setTemp"]
|
||||
self._attr_hvac_mode = HVAC_MODE_OFF
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
"""Return the current state as HVAC mode."""
|
||||
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
|
||||
self._attr_hvac_mode = HVAC_MODE_FAN_ONLY
|
||||
self.async_write_ha_state()
|
||||
return HVAC_MODE_FAN_ONLY
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return self._zone["measuredTemp"]
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the target temperature."""
|
||||
return self._zone["setTemp"]
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set the HVAC Mode and State."""
|
||||
|
|
|
@ -45,7 +45,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
class AdvantageAirTimeTo(AdvantageAirEntity, SensorEntity):
|
||||
"""Representation of Advantage Air timer control."""
|
||||
|
||||
_attr_unit_of_measurement = ADVANTAGE_AIR_SET_COUNTDOWN_UNIT
|
||||
_attr_native_unit_of_measurement = ADVANTAGE_AIR_SET_COUNTDOWN_UNIT
|
||||
|
||||
def __init__(self, instance, ac_key, action):
|
||||
"""Initialize the Advantage Air timer control."""
|
||||
|
@ -58,7 +58,7 @@ class AdvantageAirTimeTo(AdvantageAirEntity, SensorEntity):
|
|||
)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the current value."""
|
||||
return self._ac[self._time_key]
|
||||
|
||||
|
@ -78,7 +78,7 @@ class AdvantageAirTimeTo(AdvantageAirEntity, SensorEntity):
|
|||
class AdvantageAirZoneVent(AdvantageAirEntity, SensorEntity):
|
||||
"""Representation of Advantage Air Zone Vent Sensor."""
|
||||
|
||||
_attr_unit_of_measurement = PERCENTAGE
|
||||
_attr_native_unit_of_measurement = PERCENTAGE
|
||||
_attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
|
||||
def __init__(self, instance, ac_key, zone_key):
|
||||
|
@ -90,7 +90,7 @@ class AdvantageAirZoneVent(AdvantageAirEntity, SensorEntity):
|
|||
)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the current value of the air vent."""
|
||||
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
|
||||
return self._zone["value"]
|
||||
|
@ -107,19 +107,19 @@ class AdvantageAirZoneVent(AdvantageAirEntity, SensorEntity):
|
|||
class AdvantageAirZoneSignal(AdvantageAirEntity, SensorEntity):
|
||||
"""Representation of Advantage Air Zone wireless signal sensor."""
|
||||
|
||||
_attr_unit_of_measurement = PERCENTAGE
|
||||
_attr_native_unit_of_measurement = PERCENTAGE
|
||||
_attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
|
||||
def __init__(self, instance, ac_key, zone_key):
|
||||
"""Initialize an Advantage Air Zone wireless signal sensor."""
|
||||
super().__init__(instance, ac_key, zone_key=zone_key)
|
||||
super().__init__(instance, ac_key, zone_key)
|
||||
self._attr_name = f'{self._zone["name"]} Signal'
|
||||
self._attr_unique_id = (
|
||||
f'{self.coordinator.data["system"]["rid"]}-{ac_key}-{zone_key}-signal'
|
||||
)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the current value of the wireless signal."""
|
||||
return self._zone["rssi"]
|
||||
|
||||
|
@ -140,7 +140,7 @@ class AdvantageAirZoneSignal(AdvantageAirEntity, SensorEntity):
|
|||
class AdvantageAirZoneTemp(AdvantageAirEntity, SensorEntity):
|
||||
"""Representation of Advantage Air Zone wireless signal sensor."""
|
||||
|
||||
_attr_unit_of_measurement = TEMP_CELSIUS
|
||||
_attr_native_unit_of_measurement = TEMP_CELSIUS
|
||||
_attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
_attr_icon = "mdi:thermometer"
|
||||
_attr_entity_registry_enabled_default = False
|
||||
|
@ -149,9 +149,11 @@ class AdvantageAirZoneTemp(AdvantageAirEntity, SensorEntity):
|
|||
"""Initialize an Advantage Air Zone Temp Sensor."""
|
||||
super().__init__(instance, ac_key, zone_key)
|
||||
self._attr_name = f'{self._zone["name"]} Temperature'
|
||||
self._attr_unique_id = f'{self.coordinator.data["system"]["rid"]}-{self.ac_key}-{self.zone_key}-temp'
|
||||
self._attr_unique_id = (
|
||||
f'{self.coordinator.data["system"]["rid"]}-{ac_key}-{zone_key}-temp'
|
||||
)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the current value of the measured temperature."""
|
||||
return self._zone["measuredTemp"]
|
||||
|
|
|
@ -85,7 +85,7 @@ class AbstractAemetSensor(CoordinatorEntity, SensorEntity):
|
|||
self._attr_name = f"{self._name} {self._sensor_name}"
|
||||
self._attr_unique_id = self._unique_id
|
||||
self._attr_device_class = sensor_configuration.get(SENSOR_DEVICE_CLASS)
|
||||
self._attr_unit_of_measurement = sensor_configuration.get(SENSOR_UNIT)
|
||||
self._attr_native_unit_of_measurement = sensor_configuration.get(SENSOR_UNIT)
|
||||
|
||||
|
||||
class AemetSensor(AbstractAemetSensor):
|
||||
|
@ -106,7 +106,7 @@ class AemetSensor(AbstractAemetSensor):
|
|||
self._weather_coordinator = weather_coordinator
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
return self._weather_coordinator.data.get(self._sensor_type)
|
||||
|
||||
|
@ -134,7 +134,7 @@ class AemetForecastSensor(AbstractAemetSensor):
|
|||
)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
forecast = None
|
||||
forecasts = self._weather_coordinator.data.get(
|
||||
|
|
|
@ -109,7 +109,7 @@ async def async_setup_platform(
|
|||
class AfterShipSensor(SensorEntity):
|
||||
"""Representation of a AfterShip sensor."""
|
||||
|
||||
_attr_unit_of_measurement: str = "packages"
|
||||
_attr_native_unit_of_measurement: str = "packages"
|
||||
_attr_icon: str = ICON
|
||||
|
||||
def __init__(self, aftership: Tracking, name: str) -> None:
|
||||
|
@ -120,7 +120,7 @@ class AfterShipSensor(SensorEntity):
|
|||
self._attr_name = name
|
||||
|
||||
@property
|
||||
def state(self) -> int | None:
|
||||
def native_value(self) -> int | None:
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
|
|
|
@ -67,8 +67,6 @@ async def async_setup_entry(
|
|||
class AgentCamera(MjpegCamera):
|
||||
"""Representation of an Agent Device Stream."""
|
||||
|
||||
_attr_supported_features = SUPPORT_ON_OFF
|
||||
|
||||
def __init__(self, device):
|
||||
"""Initialize as a subclass of MjpegCamera."""
|
||||
device_info = {
|
||||
|
@ -80,7 +78,6 @@ class AgentCamera(MjpegCamera):
|
|||
self._removed = False
|
||||
self._attr_name = f"{device.client.name} {device.name}"
|
||||
self._attr_unique_id = f"{device._client.unique}_{device.typeID}_{device.id}"
|
||||
self._attr_should_poll = True
|
||||
super().__init__(device_info)
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(AGENT_DOMAIN, self.unique_id)},
|
||||
|
@ -102,10 +99,10 @@ class AgentCamera(MjpegCamera):
|
|||
if self.device.client.is_available and not self._removed:
|
||||
_LOGGER.error("%s lost", self.name)
|
||||
self._removed = True
|
||||
self._attr_available = self.device.client.is_available
|
||||
self._attr_icon = "mdi:camcorder-off"
|
||||
if self.is_on:
|
||||
self._attr_icon = "mdi:camcorder"
|
||||
self._attr_available = self.device.client.is_available
|
||||
self._attr_extra_state_attributes = {
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
"editable": False,
|
||||
|
@ -117,6 +114,11 @@ class AgentCamera(MjpegCamera):
|
|||
"alerts_enabled": self.device.alerts_active,
|
||||
}
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Update the state periodically."""
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_recording(self) -> bool:
|
||||
"""Return whether the monitor is recording."""
|
||||
|
@ -137,6 +139,11 @@ class AgentCamera(MjpegCamera):
|
|||
"""Return True if entity is connected."""
|
||||
return self.device.connected
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Return supported features."""
|
||||
return SUPPORT_ON_OFF
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if on."""
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
"data": {
|
||||
"host": "Hoszt",
|
||||
"port": "Port"
|
||||
}
|
||||
},
|
||||
"title": "\u00c1ll\u00edtsa be az Agent DVR-t"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u8bbe\u5907\u5df2\u88ab\u914d\u7f6e"
|
||||
},
|
||||
"error": {
|
||||
"already_in_progress": "\u914d\u7f6e\u6d41\u5df2\u8fdb\u884c\u4e2d",
|
||||
"cannot_connect": "\u8fde\u63a5\u5931\u8d25"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u4e3b\u673a\u5730\u5740",
|
||||
"port": "\u7aef\u53e3"
|
||||
},
|
||||
"title": "\u914d\u7f6e Agent DVR"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,11 @@ from typing import Final
|
|||
from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
DEVICE_CLASS_AQI,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_PM1,
|
||||
DEVICE_CLASS_PM10,
|
||||
DEVICE_CLASS_PM25,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
PERCENTAGE,
|
||||
|
@ -49,35 +53,36 @@ NO_AIRLY_SENSORS: Final = "There are no Airly sensors in this area yet."
|
|||
SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_CAQI,
|
||||
device_class=DEVICE_CLASS_AQI,
|
||||
name=ATTR_API_CAQI,
|
||||
unit_of_measurement="CAQI",
|
||||
native_unit_of_measurement="CAQI",
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM1,
|
||||
icon="mdi:blur",
|
||||
device_class=DEVICE_CLASS_PM1,
|
||||
name=ATTR_API_PM1,
|
||||
unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM25,
|
||||
icon="mdi:blur",
|
||||
device_class=DEVICE_CLASS_PM25,
|
||||
name="PM2.5",
|
||||
unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM10,
|
||||
icon="mdi:blur",
|
||||
device_class=DEVICE_CLASS_PM10,
|
||||
name=ATTR_API_PM10,
|
||||
unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_HUMIDITY,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
name=ATTR_API_HUMIDITY.capitalize(),
|
||||
unit_of_measurement=PERCENTAGE,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
value=lambda value: round(value, 1),
|
||||
),
|
||||
|
@ -85,14 +90,14 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
|||
key=ATTR_API_PRESSURE,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
name=ATTR_API_PRESSURE.capitalize(),
|
||||
unit_of_measurement=PRESSURE_HPA,
|
||||
native_unit_of_measurement=PRESSURE_HPA,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
name=ATTR_API_TEMPERATURE.capitalize(),
|
||||
unit_of_measurement=TEMP_CELSIUS,
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
value=lambda value: round(value, 1),
|
||||
),
|
||||
|
|
|
@ -84,7 +84,7 @@ class AirlySensor(CoordinatorEntity, SensorEntity):
|
|||
self.entity_description = description
|
||||
|
||||
@property
|
||||
def state(self) -> StateType:
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state."""
|
||||
state = self.coordinator.data[self.entity_description.key]
|
||||
return cast(StateType, self.entity_description.value(state))
|
||||
|
|
|
@ -72,11 +72,11 @@ class AirNowSensor(CoordinatorEntity, SensorEntity):
|
|||
self._attr_name = f"AirNow {SENSOR_TYPES[self.kind][ATTR_LABEL]}"
|
||||
self._attr_icon = SENSOR_TYPES[self.kind][ATTR_ICON]
|
||||
self._attr_device_class = SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS]
|
||||
self._attr_unit_of_measurement = SENSOR_TYPES[self.kind][ATTR_UNIT]
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[self.kind][ATTR_UNIT]
|
||||
self._attr_unique_id = f"{self.coordinator.latitude}-{self.coordinator.longitude}-{self.kind.lower()}"
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state."""
|
||||
self._state = self.coordinator.data[self.kind]
|
||||
return self._state
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
"""The AirTouch4 integration."""
|
||||
import logging
|
||||
|
||||
from airtouch4pyapi import AirTouch
|
||||
from airtouch4pyapi.airtouch import AirTouchStatus
|
||||
|
||||
from homeassistant.components.climate import SCAN_INTERVAL
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = ["climate"]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up AirTouch4 from a config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
host = entry.data[CONF_HOST]
|
||||
airtouch = AirTouch(host)
|
||||
await airtouch.UpdateInfo()
|
||||
info = airtouch.GetAcs()
|
||||
if not info:
|
||||
raise ConfigEntryNotReady
|
||||
coordinator = AirtouchDataUpdateCoordinator(hass, airtouch)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
class AirtouchDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Class to manage fetching Airtouch data."""
|
||||
|
||||
def __init__(self, hass, airtouch):
|
||||
"""Initialize global Airtouch data updater."""
|
||||
self.airtouch = airtouch
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=SCAN_INTERVAL,
|
||||
)
|
||||
|
||||
async def _async_update_data(self):
|
||||
"""Fetch data from Airtouch."""
|
||||
await self.airtouch.UpdateInfo()
|
||||
if self.airtouch.Status != AirTouchStatus.OK:
|
||||
raise UpdateFailed("Airtouch connection issue")
|
||||
return {
|
||||
"acs": [
|
||||
{"ac_number": ac.AcNumber, "is_on": ac.IsOn}
|
||||
for ac in self.airtouch.GetAcs()
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"group_number": group.GroupNumber,
|
||||
"group_name": group.GroupName,
|
||||
"is_on": group.IsOn,
|
||||
}
|
||||
for group in self.airtouch.GetGroups()
|
||||
],
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
"""AirTouch 4 component to control of AirTouch 4 Climate Devices."""
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.components.climate.const import (
|
||||
FAN_AUTO,
|
||||
FAN_DIFFUSE,
|
||||
FAN_FOCUS,
|
||||
FAN_HIGH,
|
||||
FAN_LOW,
|
||||
FAN_MEDIUM,
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_DRY,
|
||||
HVAC_MODE_FAN_ONLY,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_OFF,
|
||||
SUPPORT_FAN_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE
|
||||
AT_TO_HA_STATE = {
|
||||
"Heat": HVAC_MODE_HEAT,
|
||||
"Cool": HVAC_MODE_COOL,
|
||||
"AutoHeat": HVAC_MODE_AUTO, # airtouch reports either autoheat or autocool
|
||||
"AutoCool": HVAC_MODE_AUTO,
|
||||
"Auto": HVAC_MODE_AUTO,
|
||||
"Dry": HVAC_MODE_DRY,
|
||||
"Fan": HVAC_MODE_FAN_ONLY,
|
||||
}
|
||||
|
||||
HA_STATE_TO_AT = {
|
||||
HVAC_MODE_HEAT: "Heat",
|
||||
HVAC_MODE_COOL: "Cool",
|
||||
HVAC_MODE_AUTO: "Auto",
|
||||
HVAC_MODE_DRY: "Dry",
|
||||
HVAC_MODE_FAN_ONLY: "Fan",
|
||||
HVAC_MODE_OFF: "Off",
|
||||
}
|
||||
|
||||
AT_TO_HA_FAN_SPEED = {
|
||||
"Quiet": FAN_DIFFUSE,
|
||||
"Low": FAN_LOW,
|
||||
"Medium": FAN_MEDIUM,
|
||||
"High": FAN_HIGH,
|
||||
"Powerful": FAN_FOCUS,
|
||||
"Auto": FAN_AUTO,
|
||||
"Turbo": "turbo",
|
||||
}
|
||||
|
||||
AT_GROUP_MODES = [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY]
|
||||
|
||||
HA_FAN_SPEED_TO_AT = {value: key for key, value in AT_TO_HA_FAN_SPEED.items()}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Airtouch 4."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
info = coordinator.data
|
||||
entities = [
|
||||
AirtouchGroup(coordinator, group["group_number"], info)
|
||||
for group in info["groups"]
|
||||
] + [AirtouchAC(coordinator, ac["ac_number"], info) for ac in info["acs"]]
|
||||
|
||||
_LOGGER.debug(" Found entities %s", entities)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class AirtouchAC(CoordinatorEntity, ClimateEntity):
|
||||
"""Representation of an AirTouch 4 ac."""
|
||||
|
||||
_attr_supported_features = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE
|
||||
_attr_temperature_unit = TEMP_CELSIUS
|
||||
|
||||
def __init__(self, coordinator, ac_number, info):
|
||||
"""Initialize the climate device."""
|
||||
super().__init__(coordinator)
|
||||
self._ac_number = ac_number
|
||||
self._airtouch = coordinator.airtouch
|
||||
self._info = info
|
||||
self._unit = self._airtouch.GetAcs()[self._ac_number]
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
self._unit = self._airtouch.GetAcs()[self._ac_number]
|
||||
return super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device info for this device."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.unique_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "Airtouch",
|
||||
"model": "Airtouch 4",
|
||||
}
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return unique ID for this device."""
|
||||
return f"ac_{self._ac_number}"
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return self._unit.Temperature
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the climate device."""
|
||||
return f"AC {self._ac_number}"
|
||||
|
||||
@property
|
||||
def fan_mode(self):
|
||||
"""Return fan mode of the AC this group belongs to."""
|
||||
return AT_TO_HA_FAN_SPEED[self._airtouch.acs[self._ac_number].AcFanSpeed]
|
||||
|
||||
@property
|
||||
def fan_modes(self):
|
||||
"""Return the list of available fan modes."""
|
||||
airtouch_fan_speeds = self._airtouch.GetSupportedFanSpeedsForAc(self._ac_number)
|
||||
return [AT_TO_HA_FAN_SPEED[speed] for speed in airtouch_fan_speeds]
|
||||
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
"""Return hvac target hvac state."""
|
||||
is_off = self._unit.PowerState == "Off"
|
||||
if is_off:
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
return AT_TO_HA_STATE[self._airtouch.acs[self._ac_number].AcMode]
|
||||
|
||||
@property
|
||||
def hvac_modes(self):
|
||||
"""Return the list of available operation modes."""
|
||||
airtouch_modes = self._airtouch.GetSupportedCoolingModesForAc(self._ac_number)
|
||||
modes = [AT_TO_HA_STATE[mode] for mode in airtouch_modes]
|
||||
modes.append(HVAC_MODE_OFF)
|
||||
return modes
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set new operation mode."""
|
||||
if hvac_mode not in HA_STATE_TO_AT:
|
||||
raise ValueError(f"Unsupported HVAC mode: {hvac_mode}")
|
||||
|
||||
if hvac_mode == HVAC_MODE_OFF:
|
||||
return await self.async_turn_off()
|
||||
await self._airtouch.SetCoolingModeForAc(
|
||||
self._ac_number, HA_STATE_TO_AT[hvac_mode]
|
||||
)
|
||||
# in case it isn't already, unless the HVAC mode was off, then the ac should be on
|
||||
await self.async_turn_on()
|
||||
self._unit = self._airtouch.GetAcs()[self._ac_number]
|
||||
_LOGGER.debug("Setting operation mode of %s to %s", self._ac_number, hvac_mode)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode):
|
||||
"""Set new fan mode."""
|
||||
if fan_mode not in self.fan_modes:
|
||||
raise ValueError(f"Unsupported fan mode: {fan_mode}")
|
||||
|
||||
_LOGGER.debug("Setting fan mode of %s to %s", self._ac_number, fan_mode)
|
||||
await self._airtouch.SetFanSpeedForAc(
|
||||
self._ac_number, HA_FAN_SPEED_TO_AT[fan_mode]
|
||||
)
|
||||
self._unit = self._airtouch.GetAcs()[self._ac_number]
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self):
|
||||
"""Turn on."""
|
||||
_LOGGER.debug("Turning %s on", self.unique_id)
|
||||
# in case ac is not on. Airtouch turns itself off if no groups are turned on
|
||||
# (even if groups turned back on)
|
||||
await self._airtouch.TurnAcOn(self._ac_number)
|
||||
|
||||
async def async_turn_off(self):
|
||||
"""Turn off."""
|
||||
_LOGGER.debug("Turning %s off", self.unique_id)
|
||||
await self._airtouch.TurnAcOff(self._ac_number)
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
class AirtouchGroup(CoordinatorEntity, ClimateEntity):
|
||||
"""Representation of an AirTouch 4 group."""
|
||||
|
||||
_attr_supported_features = SUPPORT_TARGET_TEMPERATURE
|
||||
_attr_temperature_unit = TEMP_CELSIUS
|
||||
_attr_hvac_modes = AT_GROUP_MODES
|
||||
|
||||
def __init__(self, coordinator, group_number, info):
|
||||
"""Initialize the climate device."""
|
||||
super().__init__(coordinator)
|
||||
self._group_number = group_number
|
||||
self._airtouch = coordinator.airtouch
|
||||
self._info = info
|
||||
self._unit = self._airtouch.GetGroupByGroupNumber(self._group_number)
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
self._unit = self._airtouch.GetGroupByGroupNumber(self._group_number)
|
||||
return super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device info for this device."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.unique_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "Airtouch",
|
||||
"model": "Airtouch 4",
|
||||
}
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return unique ID for this device."""
|
||||
return self._group_number
|
||||
|
||||
@property
|
||||
def min_temp(self):
|
||||
"""Return Minimum Temperature for AC of this group."""
|
||||
return self._airtouch.acs[self._unit.BelongsToAc].MinSetpoint
|
||||
|
||||
@property
|
||||
def max_temp(self):
|
||||
"""Return Max Temperature for AC of this group."""
|
||||
return self._airtouch.acs[self._unit.BelongsToAc].MaxSetpoint
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the climate device."""
|
||||
return self._unit.GroupName
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return self._unit.Temperature
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the temperature we are trying to reach."""
|
||||
return self._unit.TargetSetpoint
|
||||
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
"""Return hvac target hvac state."""
|
||||
# there are other power states that aren't 'on' but still count as on (eg. 'Turbo')
|
||||
is_off = self._unit.PowerState == "Off"
|
||||
if is_off:
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
return HVAC_MODE_FAN_ONLY
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set new operation mode."""
|
||||
if hvac_mode not in HA_STATE_TO_AT:
|
||||
raise ValueError(f"Unsupported HVAC mode: {hvac_mode}")
|
||||
|
||||
if hvac_mode == HVAC_MODE_OFF:
|
||||
return await self.async_turn_off()
|
||||
if self.hvac_mode == HVAC_MODE_OFF:
|
||||
await self.async_turn_on()
|
||||
self._unit = self._airtouch.GetGroups()[self._group_number]
|
||||
_LOGGER.debug(
|
||||
"Setting operation mode of %s to %s", self._group_number, hvac_mode
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def fan_mode(self):
|
||||
"""Return fan mode of the AC this group belongs to."""
|
||||
return AT_TO_HA_FAN_SPEED[self._airtouch.acs[self._unit.BelongsToAc].AcFanSpeed]
|
||||
|
||||
@property
|
||||
def fan_modes(self):
|
||||
"""Return the list of available fan modes."""
|
||||
airtouch_fan_speeds = self._airtouch.GetSupportedFanSpeedsByGroup(
|
||||
self._group_number
|
||||
)
|
||||
return [AT_TO_HA_FAN_SPEED[speed] for speed in airtouch_fan_speeds]
|
||||
|
||||
async def async_set_temperature(self, **kwargs):
|
||||
"""Set new target temperatures."""
|
||||
temp = kwargs.get(ATTR_TEMPERATURE)
|
||||
|
||||
_LOGGER.debug("Setting temp of %s to %s", self._group_number, str(temp))
|
||||
self._unit = await self._airtouch.SetGroupToTemperature(
|
||||
self._group_number, int(temp)
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode):
|
||||
"""Set new fan mode."""
|
||||
if fan_mode not in self.fan_modes:
|
||||
raise ValueError(f"Unsupported fan mode: {fan_mode}")
|
||||
|
||||
_LOGGER.debug("Setting fan mode of %s to %s", self._group_number, fan_mode)
|
||||
self._unit = await self._airtouch.SetFanSpeedByGroup(
|
||||
self._group_number, HA_FAN_SPEED_TO_AT[fan_mode]
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self):
|
||||
"""Turn on."""
|
||||
_LOGGER.debug("Turning %s on", self.unique_id)
|
||||
await self._airtouch.TurnGroupOn(self._group_number)
|
||||
|
||||
# in case ac is not on. Airtouch turns itself off if no groups are turned on
|
||||
# (even if groups turned back on)
|
||||
await self._airtouch.TurnAcOn(
|
||||
self._airtouch.GetGroupByGroupNumber(self._group_number).BelongsToAc
|
||||
)
|
||||
# this might cause the ac object to be wrong, so force the shared data
|
||||
# store to update
|
||||
await self.coordinator.async_request_refresh()
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self):
|
||||
"""Turn off."""
|
||||
_LOGGER.debug("Turning %s off", self.unique_id)
|
||||
await self._airtouch.TurnGroupOff(self._group_number)
|
||||
# this will cause the ac object to be wrong
|
||||
# (ac turns off automatically if no groups are running)
|
||||
# so force the shared data store to update
|
||||
await self.coordinator.async_request_refresh()
|
||||
self.async_write_ha_state()
|
|
@ -0,0 +1,50 @@
|
|||
"""Config flow for AirTouch4."""
|
||||
from airtouch4pyapi import AirTouch, AirTouchStatus
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_HOST
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
DATA_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str})
|
||||
|
||||
|
||||
class AirtouchConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle an Airtouch config flow."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow initialized by the user."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(step_id="user", data_schema=DATA_SCHEMA)
|
||||
|
||||
errors = {}
|
||||
|
||||
host = user_input[CONF_HOST]
|
||||
self._async_abort_entries_match({CONF_HOST: host})
|
||||
|
||||
airtouch = AirTouch(host)
|
||||
await airtouch.UpdateInfo()
|
||||
airtouch_status = airtouch.Status
|
||||
airtouch_has_groups = bool(
|
||||
airtouch.Status == AirTouchStatus.OK and airtouch.GetGroups()
|
||||
)
|
||||
|
||||
if airtouch_status != AirTouchStatus.OK:
|
||||
errors["base"] = "cannot_connect"
|
||||
elif not airtouch_has_groups:
|
||||
errors["base"] = "no_units"
|
||||
|
||||
if errors:
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=DATA_SCHEMA, errors=errors
|
||||
)
|
||||
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_HOST],
|
||||
data={
|
||||
CONF_HOST: user_input[CONF_HOST],
|
||||
},
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
"""Constants for the AirTouch4 integration."""
|
||||
|
||||
DOMAIN = "airtouch4"
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"domain": "airtouch4",
|
||||
"name": "AirTouch 4",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/airtouch4",
|
||||
"requirements": [
|
||||
"airtouch4pyapi==1.0.5"
|
||||
],
|
||||
"codeowners": [
|
||||
"@LonePurpleWolf"
|
||||
],
|
||||
"iot_class": "local_polling"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"no_units": "Could not find any AirTouch 4 Groups."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Setup your AirTouch 4 connection details.",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "El dispositiu ja est\u00e0 configurat"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Ha fallat la connexi\u00f3",
|
||||
"no_units": "No s'han trobat grups AirTouch 4."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Amfitri\u00f3"
|
||||
},
|
||||
"title": "Configura els detalls de connexi\u00f3 d'AirTouch 4."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Hostitel"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Ger\u00e4t ist bereits konfiguriert"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Verbindung fehlgeschlagen",
|
||||
"no_units": "Es konnten keine AirTouch 4-Gruppen gefunden werden."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
},
|
||||
"title": "Richte deine AirTouch 4-Verbindungsdetails ein."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect",
|
||||
"no_units": "Could not find any AirTouch 4 Groups."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
},
|
||||
"title": "Setup your AirTouch 4 connection details."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Seade on juba h\u00e4\u00e4lestatud"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00dchendamine nurjus",
|
||||
"no_units": "Ei leidnud \u00fchtegi AirTouch 4 gruppi."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
},
|
||||
"title": "AirTouch 4 \u00fchenduse \u00fcksikasjade seadistamine."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Impossibile connettersi",
|
||||
"no_units": "Impossibile trovare alcun gruppo AirTouch 4."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
},
|
||||
"title": "Imposta i dettagli della connessione AirTouch 4."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Apparaat is al geconfigureerd"
|
||||
},
|
||||
"error": {
|
||||
"no_units": "Kan geen AirTouch 4-groepen vinden."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.",
|
||||
"no_units": "\u0413\u0440\u0443\u043f\u043f\u044b AirTouch 4 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u0425\u043e\u0441\u0442"
|
||||
},
|
||||
"title": "AirTouch 4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u9023\u7dda\u5931\u6557",
|
||||
"no_units": "\u627e\u4e0d\u5230\u4efb\u4f55 AirTouch 4 \u7fa4\u7d44\u3002"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u4e3b\u6a5f\u7aef"
|
||||
},
|
||||
"title": "\u8a2d\u5b9a AirTouch 4 \u9023\u7dda\u8cc7\u8a0a\u3002"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -212,7 +212,7 @@ class AirVisualGeographySensor(AirVisualEntity, SensorEntity):
|
|||
self._attr_icon = icon
|
||||
self._attr_name = f"{GEOGRAPHY_SENSOR_LOCALES[locale]} {name}"
|
||||
self._attr_unique_id = f"{config_entry.unique_id}_{locale}_{kind}"
|
||||
self._attr_unit_of_measurement = unit
|
||||
self._attr_native_unit_of_measurement = unit
|
||||
self._config_entry = config_entry
|
||||
self._kind = kind
|
||||
self._locale = locale
|
||||
|
@ -232,16 +232,16 @@ class AirVisualGeographySensor(AirVisualEntity, SensorEntity):
|
|||
|
||||
if self._kind == SENSOR_KIND_LEVEL:
|
||||
aqi = data[f"aqi{self._locale}"]
|
||||
[(self._attr_state, self._attr_icon)] = [
|
||||
[(self._attr_native_value, self._attr_icon)] = [
|
||||
(name, icon)
|
||||
for (floor, ceiling), (name, icon) in POLLUTANT_LEVELS.items()
|
||||
if floor <= aqi <= ceiling
|
||||
]
|
||||
elif self._kind == SENSOR_KIND_AQI:
|
||||
self._attr_state = data[f"aqi{self._locale}"]
|
||||
self._attr_native_value = data[f"aqi{self._locale}"]
|
||||
elif self._kind == SENSOR_KIND_POLLUTANT:
|
||||
symbol = data[f"main{self._locale}"]
|
||||
self._attr_state = symbol
|
||||
self._attr_native_value = symbol
|
||||
self._attr_extra_state_attributes.update(
|
||||
{
|
||||
ATTR_POLLUTANT_SYMBOL: symbol,
|
||||
|
@ -298,7 +298,7 @@ class AirVisualNodeProSensor(AirVisualEntity, SensorEntity):
|
|||
f"{coordinator.data['settings']['node_name']} Node/Pro: {name}"
|
||||
)
|
||||
self._attr_unique_id = f"{coordinator.data['serial_number']}_{kind}"
|
||||
self._attr_unit_of_measurement = unit
|
||||
self._attr_native_unit_of_measurement = unit
|
||||
self._kind = kind
|
||||
|
||||
@property
|
||||
|
@ -320,24 +320,30 @@ class AirVisualNodeProSensor(AirVisualEntity, SensorEntity):
|
|||
"""Update the entity from the latest data."""
|
||||
if self._kind == SENSOR_KIND_AQI:
|
||||
if self.coordinator.data["settings"]["is_aqi_usa"]:
|
||||
self._attr_state = self.coordinator.data["measurements"]["aqi_us"]
|
||||
self._attr_native_value = self.coordinator.data["measurements"][
|
||||
"aqi_us"
|
||||
]
|
||||
else:
|
||||
self._attr_state = self.coordinator.data["measurements"]["aqi_cn"]
|
||||
self._attr_native_value = self.coordinator.data["measurements"][
|
||||
"aqi_cn"
|
||||
]
|
||||
elif self._kind == SENSOR_KIND_BATTERY_LEVEL:
|
||||
self._attr_state = self.coordinator.data["status"]["battery"]
|
||||
self._attr_native_value = self.coordinator.data["status"]["battery"]
|
||||
elif self._kind == SENSOR_KIND_CO2:
|
||||
self._attr_state = self.coordinator.data["measurements"].get("co2")
|
||||
self._attr_native_value = self.coordinator.data["measurements"].get("co2")
|
||||
elif self._kind == SENSOR_KIND_HUMIDITY:
|
||||
self._attr_state = self.coordinator.data["measurements"].get("humidity")
|
||||
self._attr_native_value = self.coordinator.data["measurements"].get(
|
||||
"humidity"
|
||||
)
|
||||
elif self._kind == SENSOR_KIND_PM_0_1:
|
||||
self._attr_state = self.coordinator.data["measurements"].get("pm0_1")
|
||||
self._attr_native_value = self.coordinator.data["measurements"].get("pm0_1")
|
||||
elif self._kind == SENSOR_KIND_PM_1_0:
|
||||
self._attr_state = self.coordinator.data["measurements"].get("pm1_0")
|
||||
self._attr_native_value = self.coordinator.data["measurements"].get("pm1_0")
|
||||
elif self._kind == SENSOR_KIND_PM_2_5:
|
||||
self._attr_state = self.coordinator.data["measurements"].get("pm2_5")
|
||||
self._attr_native_value = self.coordinator.data["measurements"].get("pm2_5")
|
||||
elif self._kind == SENSOR_KIND_TEMPERATURE:
|
||||
self._attr_state = self.coordinator.data["measurements"].get(
|
||||
self._attr_native_value = self.coordinator.data["measurements"].get(
|
||||
"temperature_C"
|
||||
)
|
||||
elif self._kind == SENSOR_KIND_VOC:
|
||||
self._attr_state = self.coordinator.data["measurements"].get("voc")
|
||||
self._attr_native_value = self.coordinator.data["measurements"].get("voc")
|
||||
|
|
|
@ -34,13 +34,29 @@
|
|||
"data": {
|
||||
"ip_address": "Hoszt",
|
||||
"password": "Jelsz\u00f3"
|
||||
}
|
||||
},
|
||||
"description": "Szem\u00e9lyes AirVisual egys\u00e9g figyel\u00e9se. A jelsz\u00f3 lek\u00e9rhet\u0151 a k\u00e9sz\u00fcl\u00e9k felhaszn\u00e1l\u00f3i fel\u00fclet\u00e9r\u0151l.",
|
||||
"title": "AirVisual Node/Pro konfigur\u00e1l\u00e1sa"
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "API kulcs"
|
||||
},
|
||||
"title": "Az AirVisual \u00fajb\u00f3li hiteles\u00edt\u00e9se"
|
||||
},
|
||||
"user": {
|
||||
"description": "V\u00e1lassza ki, hogy milyen t\u00edpus\u00fa AirVisual adatokat szeretne figyelni.",
|
||||
"title": "Az AirVisual konfigur\u00e1l\u00e1sa"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"show_on_map": "A megfigyelt f\u00f6ldrajz megjelen\u00edt\u00e9se a t\u00e9rk\u00e9pen"
|
||||
},
|
||||
"title": "Az AirVisual konfigur\u00e1l\u00e1sa"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"co": "Oxid uhelnat\u00fd",
|
||||
"n2": "Oxid dusi\u010dit\u00fd",
|
||||
"o3": "Oz\u00f3n",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2,5",
|
||||
"s2": "Oxid si\u0159i\u010dit\u00fd"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "Dobr\u00e9",
|
||||
"hazardous": "Riskantn\u00ed",
|
||||
"moderate": "M\u00edrn\u00e9",
|
||||
"unhealthy": "Nezdrav\u00e9",
|
||||
"unhealthy_sensitive": "Nezdrav\u00e9 pro citliv\u00e9 skupiny",
|
||||
"very_unhealthy": "Velmi nezdrav\u00e9"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"co": "Mon\u00f3xido de carbono",
|
||||
"n2": "Di\u00f3xido de nitr\u00f3geno",
|
||||
"o3": "Ozono",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5",
|
||||
"s2": "Di\u00f3xido de azufre"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "Bien",
|
||||
"hazardous": "Peligroso",
|
||||
"moderate": "Moderado",
|
||||
"unhealthy": "Insalubre",
|
||||
"unhealthy_sensitive": "Incorrecto para grupos sensibles",
|
||||
"very_unhealthy": "Muy poco saludable"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"co": "Sz\u00e9n-monoxid",
|
||||
"n2": "Nitrog\u00e9n-dioxid",
|
||||
"o3": "\u00d3zon",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5",
|
||||
"s2": "K\u00e9n-dioxid"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "J\u00f3",
|
||||
"hazardous": "Vesz\u00e9lyes",
|
||||
"moderate": "M\u00e9rs\u00e9kelt",
|
||||
"unhealthy": "Eg\u00e9szs\u00e9gtelen",
|
||||
"unhealthy_sensitive": "Eg\u00e9szs\u00e9gtelen az \u00e9rz\u00e9keny csoportok sz\u00e1m\u00e1ra",
|
||||
"very_unhealthy": "Nagyon eg\u00e9szs\u00e9gtelen"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,20 @@
|
|||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"co": "Karbonmonoksid",
|
||||
"n2": "Nitrogendioksid",
|
||||
"o3": "Ozon",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5"
|
||||
"p2": "PM2.5",
|
||||
"s2": "Svoveldioksid"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "Bra",
|
||||
"hazardous": "Farlig",
|
||||
"moderate": "Moderat",
|
||||
"unhealthy": "Usunt",
|
||||
"unhealthy_sensitive": "Usunt for sensitive grupper",
|
||||
"very_unhealthy": "Veldig usunt"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,7 +49,10 @@ def setup_platform(
|
|||
try:
|
||||
if not acc.login():
|
||||
raise ValueError("Username or Password is incorrect")
|
||||
add_entities(AladdinDevice(acc, door) for door in acc.get_doors())
|
||||
add_entities(
|
||||
(AladdinDevice(acc, door) for door in acc.get_doors()),
|
||||
update_before_add=True,
|
||||
)
|
||||
except (TypeError, KeyError, NameError, ValueError) as ex:
|
||||
_LOGGER.error("%s", ex)
|
||||
hass.components.persistent_notification.create(
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"is_armed_away": "{entity_name} est arm\u00e9",
|
||||
"is_armed_home": "{entity_name} est arm\u00e9 \u00e0 la maison",
|
||||
"is_armed_night": "{entity_name} est arm\u00e9 la nuit",
|
||||
"is_armed_vacation": "{entity_name} est arm\u00e9 en mode vacances",
|
||||
"is_disarmed": "{entity_name} est d\u00e9sarm\u00e9",
|
||||
"is_triggered": "{entity_name} est d\u00e9clench\u00e9"
|
||||
},
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
"trigger": "{entity_name} riaszt\u00e1si esem\u00e9ny ind\u00edt\u00e1sa"
|
||||
},
|
||||
"condition_type": {
|
||||
"is_armed_vacation": "{entity_name} nyaral\u00e1s \u00e9les\u00edtve"
|
||||
"is_armed_away": "{entity_name} \u00e9les\u00edtve van",
|
||||
"is_armed_home": "{entity_name} \u00e9les\u00edtett otthoni m\u00f3dban",
|
||||
"is_armed_night": "{entity_name} \u00e9les\u00edtett \u00e9jszaka m\u00f3dban",
|
||||
"is_armed_vacation": "{entity_name} nyaral\u00e1s \u00e9les\u00edtve",
|
||||
"is_disarmed": "{entity_name} hat\u00e1stalan\u00edtva",
|
||||
"is_triggered": "{entity_name} aktiv\u00e1lva van"
|
||||
},
|
||||
"trigger_type": {
|
||||
"armed_away": "{entity_name} t\u00e1voz\u00f3 m\u00f3dban lett \u00e9les\u00edtve",
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"arm_away": "Aktiver {entity_name} borte",
|
||||
"arm_home": "Aktiver {entity_name} hjemme",
|
||||
"arm_night": "Aktiver {entity_name} natt",
|
||||
"arm_vacation": "{entity_name} ferie",
|
||||
"disarm": "Deaktiver {entity_name}",
|
||||
"trigger": "Utl\u00f8ser {entity_name}"
|
||||
},
|
||||
|
@ -11,6 +12,7 @@
|
|||
"is_armed_away": "{entity_name} er aktivert borte",
|
||||
"is_armed_home": "{entity_name} er aktivert hjemme",
|
||||
"is_armed_night": "{entity_name} er aktivert natt",
|
||||
"is_armed_vacation": "{entity_name} er armert ferie",
|
||||
"is_disarmed": "{entity_name} er deaktivert",
|
||||
"is_triggered": "{entity_name} er utl\u00f8st"
|
||||
},
|
||||
|
@ -18,6 +20,7 @@
|
|||
"armed_away": "{entity_name} aktivert borte",
|
||||
"armed_home": "{entity_name} aktivert hjemme",
|
||||
"armed_night": "{entity_name} aktivert natt",
|
||||
"armed_vacation": "{entity_name} armert ferie",
|
||||
"disarmed": "{entity_name} deaktivert",
|
||||
"triggered": "{entity_name} utl\u00f8st"
|
||||
}
|
||||
|
@ -29,6 +32,7 @@
|
|||
"armed_custom_bypass": "Armert tilpasset unntak",
|
||||
"armed_home": "Armert hjemme",
|
||||
"armed_night": "Armert natt",
|
||||
"armed_vacation": "Armert ferie",
|
||||
"arming": "Armerer",
|
||||
"disarmed": "Avsl\u00e5tt",
|
||||
"disarming": "Disarmer",
|
||||
|
|
|
@ -111,13 +111,13 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
|
|||
def _fault_callback(self, zone):
|
||||
"""Update the zone's state, if needed."""
|
||||
if zone is None or int(zone) == self._zone_number:
|
||||
self._attr_state = 1
|
||||
self._attr_is_on = True
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def _restore_callback(self, zone):
|
||||
"""Update the zone's state, if needed."""
|
||||
if zone is None or (int(zone) == self._zone_number and not self._loop):
|
||||
self._attr_state = 0
|
||||
self._attr_is_on = False
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def _rfx_message_callback(self, message):
|
||||
|
@ -125,7 +125,7 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
|
|||
if self._rfid and message and message.serial_number == self._rfid:
|
||||
rfstate = message.value
|
||||
if self._loop:
|
||||
self._attr_state = 1 if message.loop[self._loop - 1] else 0
|
||||
self._attr_is_on = bool(message.loop[self._loop - 1])
|
||||
attr = {CONF_ZONE_NUMBER: self._zone_number}
|
||||
if self._rfid and rfstate is not None:
|
||||
attr[ATTR_RF_BIT0] = bool(rfstate & 0x01)
|
||||
|
@ -150,5 +150,5 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
|
|||
message.channel,
|
||||
message.value,
|
||||
)
|
||||
self._attr_state = message.value
|
||||
self._attr_is_on = bool(message.value)
|
||||
self.schedule_update_ha_state()
|
||||
|
|
|
@ -32,6 +32,6 @@ class AlarmDecoderSensor(SensorEntity):
|
|||
)
|
||||
|
||||
def _message_callback(self, message):
|
||||
if self._attr_state != message.text:
|
||||
self._attr_state = message.text
|
||||
if self._attr_native_value != message.text:
|
||||
self._attr_native_value = message.text
|
||||
self.schedule_update_ha_state()
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"error": {
|
||||
"int": "Az al\u00e1bbi mez\u0151nek eg\u00e9sz sz\u00e1mnak kell lennie.",
|
||||
"loop_range": "Az RF hurok eg\u00e9sz sz\u00e1m\u00e1nak 1 \u00e9s 4 k\u00f6z\u00f6tt kell lennie.",
|
||||
"loop_rfid": "Az RF hurok nem haszn\u00e1lhat\u00f3 RF sorozat n\u00e9lk\u00fcl.",
|
||||
"relay_inclusive": "A rel\u00e9c\u00edm \u00e9s a rel\u00e9csatorna egym\u00e1st\u00f3l f\u00fcgg, \u00e9s egy\u00fctt kell felt\u00fcntetni."
|
||||
},
|
||||
"step": {
|
||||
|
@ -55,6 +56,7 @@
|
|||
"zone_name": "Z\u00f3na neve",
|
||||
"zone_relayaddr": "Rel\u00e9 c\u00edm",
|
||||
"zone_relaychan": "Rel\u00e9 csatorna",
|
||||
"zone_rfid": "RF soros",
|
||||
"zone_type": "Z\u00f3na t\u00edpusa"
|
||||
},
|
||||
"description": "Adja meg a {zone_number} z\u00f3na adatait. {zone_number} z\u00f3na t\u00f6rl\u00e9s\u00e9hez hagyja \u00fcresen a Z\u00f3na neve elemet.",
|
||||
|
|
|
@ -99,7 +99,7 @@ class AlexaCapability:
|
|||
return False
|
||||
|
||||
@staticmethod
|
||||
def properties_non_controllable() -> bool:
|
||||
def properties_non_controllable() -> bool | None:
|
||||
"""Return True if non controllable."""
|
||||
return None
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"""Alexa related errors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .const import API_TEMP_UNITS
|
||||
|
@ -22,8 +24,8 @@ class AlexaError(Exception):
|
|||
A handler can raise subclasses of this to return an error to the request.
|
||||
"""
|
||||
|
||||
namespace = None
|
||||
error_type = None
|
||||
namespace: str | None = None
|
||||
error_type: str | None = None
|
||||
|
||||
def __init__(self, error_message, payload=None):
|
||||
"""Initialize an alexa error."""
|
||||
|
|
|
@ -112,7 +112,7 @@ class AlphaVantageSensor(SensorEntity):
|
|||
self._symbol = symbol[CONF_SYMBOL]
|
||||
self._attr_name = symbol.get(CONF_NAME, self._symbol)
|
||||
self._timeseries = timeseries
|
||||
self._attr_unit_of_measurement = symbol.get(CONF_CURRENCY, self._symbol)
|
||||
self._attr_native_unit_of_measurement = symbol.get(CONF_CURRENCY, self._symbol)
|
||||
self._attr_icon = ICONS.get(symbol.get(CONF_CURRENCY, "USD"))
|
||||
|
||||
def update(self):
|
||||
|
@ -120,7 +120,7 @@ class AlphaVantageSensor(SensorEntity):
|
|||
_LOGGER.debug("Requesting new data for symbol %s", self._symbol)
|
||||
all_values, _ = self._timeseries.get_intraday(self._symbol)
|
||||
values = next(iter(all_values.values()))
|
||||
self._attr_state = values["1. open"]
|
||||
self._attr_native_value = values["1. open"]
|
||||
self._attr_extra_state_attributes = (
|
||||
{
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
|
@ -148,7 +148,7 @@ class AlphaVantageForeignExchange(SensorEntity):
|
|||
else f"{self._to_currency}/{self._from_currency}"
|
||||
)
|
||||
self._attr_icon = ICONS.get(self._from_currency, "USD")
|
||||
self._attr_unit_of_measurement = self._to_currency
|
||||
self._attr_native_unit_of_measurement = self._to_currency
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data and updates the states."""
|
||||
|
@ -160,7 +160,7 @@ class AlphaVantageForeignExchange(SensorEntity):
|
|||
values, _ = self._foreign_exchange.get_currency_exchange_rate(
|
||||
from_currency=self._from_currency, to_currency=self._to_currency
|
||||
)
|
||||
self._attr_state = round(float(values["5. Exchange Rate"]), 4)
|
||||
self._attr_native_value = round(float(values["5. Exchange Rate"]), 4)
|
||||
self._attr_extra_state_attributes = (
|
||||
{
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
|
|
|
@ -39,38 +39,38 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
SensorEntityDescription(
|
||||
key="particulate_matter_2_5",
|
||||
name="Particulate Matter < 2.5 μm",
|
||||
unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="particulate_matter_10",
|
||||
name="Particulate Matter < 10 μm",
|
||||
unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="sulphur_dioxide",
|
||||
name="Sulphur Dioxide (SO2)",
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="nitrogen_dioxide",
|
||||
name="Nitrogen Dioxide (NO2)",
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="ozone",
|
||||
name="Ozone",
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="carbon_monoxide",
|
||||
name="Carbon Monoxide (CO)",
|
||||
device_class=DEVICE_CLASS_CO,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -85,21 +85,21 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Grass Pollen",
|
||||
icon="mdi:grass",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="tree",
|
||||
name="Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="weed",
|
||||
name="Weed Pollen",
|
||||
icon="mdi:sprout",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="grass_risk",
|
||||
|
@ -124,7 +124,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Poaceae Grass Pollen",
|
||||
icon="mdi:grass",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -132,7 +132,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Alder Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -140,7 +140,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Birch Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -148,7 +148,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Cypress Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -156,7 +156,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Elm Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -164,7 +164,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Hazel Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -172,7 +172,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Oak Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -180,7 +180,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Pine Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -188,7 +188,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Plane Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -196,7 +196,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Poplar Tree Pollen",
|
||||
icon="mdi:tree",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -204,7 +204,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Chenopod Weed Pollen",
|
||||
icon="mdi:sprout",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -212,7 +212,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Mugwort Weed Pollen",
|
||||
icon="mdi:sprout",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -220,7 +220,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Nettle Weed Pollen",
|
||||
icon="mdi:sprout",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -228,7 +228,7 @@ SENSORS: dict[str, list[SensorEntityDescription]] = {
|
|||
name="Ragweed Weed Pollen",
|
||||
icon="mdi:sprout",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_CUBIC_METER,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -66,7 +66,7 @@ class AmbeeSensorEntity(CoordinatorEntity, SensorEntity):
|
|||
}
|
||||
|
||||
@property
|
||||
def state(self) -> StateType:
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state of the sensor."""
|
||||
value = getattr(self.coordinator.data, self.entity_description.key)
|
||||
if isinstance(value, str):
|
||||
|
|
|
@ -154,8 +154,6 @@ class AmbiclimateEntity(ClimateEntity):
|
|||
"name": self.name,
|
||||
"manufacturer": "Ambiclimate",
|
||||
}
|
||||
self._attr_min_temp = heater.get_min_temp()
|
||||
self._attr_max_temp = heater.get_max_temp()
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
|
@ -184,6 +182,8 @@ class AmbiclimateEntity(ClimateEntity):
|
|||
await self._store.async_save(token_info)
|
||||
|
||||
data = await self._heater.update_device()
|
||||
self._attr_min_temp = self._heater.get_min_temp()
|
||||
self._attr_max_temp = self._heater.get_max_temp()
|
||||
self._attr_target_temperature = data.get("target_temperature")
|
||||
self._attr_current_temperature = data.get("temperature")
|
||||
self._attr_current_humidity = data.get("humidity")
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"access_token": "Ismeretlen hiba a hozz\u00e1f\u00e9r\u00e9si token gener\u00e1l\u00e1s\u00e1ban.",
|
||||
"already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van",
|
||||
"missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rlek, k\u00f6vesd a dokument\u00e1ci\u00f3t."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Sikeres hiteles\u00edt\u00e9s"
|
||||
},
|
||||
"error": {
|
||||
"follow_link": "K\u00e9rlek, k\u00f6vesd a hivatkoz\u00e1st \u00e9s hiteles\u00edtsd magad miel\u0151tt megnyomod a K\u00fcld\u00e9s gombot",
|
||||
"no_token": "Nem hiteles\u00edtett Ambiclimate"
|
||||
},
|
||||
"step": {
|
||||
"auth": {
|
||||
"description": "K\u00e9rj\u00fck, k\u00f6vesse ezt a [link] ({authorization_url} Author_url}) \u00e9s ** Enged\u00e9lyezze ** a hozz\u00e1f\u00e9r\u00e9st Ambiclimate -fi\u00f3kj\u00e1hoz, majd t\u00e9rjen vissza, \u00e9s nyomja meg az al\u00e1bbi ** K\u00fcld\u00e9s ** gombot.\n (Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a megadott visszah\u00edv\u00e1si URL {cb_url})",
|
||||
"title": "Ambiclimate hiteles\u00edt\u00e9se"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Ambient Weather Station",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/ambient_station",
|
||||
"requirements": ["aioambient==1.2.5"],
|
||||
"requirements": ["aioambient==1.2.6"],
|
||||
"codeowners": ["@bachya"],
|
||||
"iot_class": "cloud_push"
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ class AmbientWeatherSensor(AmbientWeatherEntity, SensorEntity):
|
|||
ambient, mac_address, station_name, sensor_type, sensor_name, device_class
|
||||
)
|
||||
|
||||
self._attr_unit_of_measurement = unit
|
||||
self._attr_native_unit_of_measurement = unit
|
||||
|
||||
@callback
|
||||
def update_from_latest_data(self) -> None:
|
||||
|
@ -75,10 +75,10 @@ class AmbientWeatherSensor(AmbientWeatherEntity, SensorEntity):
|
|||
].get(TYPE_SOLARRADIATION)
|
||||
|
||||
if w_m2_brightness_val is None:
|
||||
self._attr_state = None
|
||||
self._attr_native_value = None
|
||||
else:
|
||||
self._attr_state = round(float(w_m2_brightness_val) / 0.0079)
|
||||
self._attr_native_value = round(float(w_m2_brightness_val) / 0.0079)
|
||||
else:
|
||||
self._attr_state = self._ambient.stations[self._mac_address][
|
||||
self._attr_native_value = self._ambient.stations[self._mac_address][
|
||||
ATTR_LAST_DATA
|
||||
].get(self._sensor_type)
|
||||
|
|
|
@ -53,7 +53,7 @@ _CROSSLINE_DETECTED_PARAMS = (
|
|||
DEVICE_CLASS_MOTION,
|
||||
"CrossLineDetection",
|
||||
)
|
||||
BINARY_SENSORS = {
|
||||
RAW_BINARY_SENSORS = {
|
||||
BINARY_SENSOR_AUDIO_DETECTED: _AUDIO_DETECTED_PARAMS,
|
||||
BINARY_SENSOR_AUDIO_DETECTED_POLLED: _AUDIO_DETECTED_PARAMS,
|
||||
BINARY_SENSOR_MOTION_DETECTED: _MOTION_DETECTED_PARAMS,
|
||||
|
@ -64,7 +64,7 @@ BINARY_SENSORS = {
|
|||
}
|
||||
BINARY_SENSORS = {
|
||||
k: dict(zip((SENSOR_NAME, SENSOR_DEVICE_CLASS, SENSOR_EVENT_CODE), v))
|
||||
for k, v in BINARY_SENSORS.items()
|
||||
for k, v in RAW_BINARY_SENSORS.items()
|
||||
}
|
||||
_EXCLUSIVE_OPTIONS = [
|
||||
{BINARY_SENSOR_MOTION_DETECTED, BINARY_SENSOR_MOTION_DETECTED_POLLED},
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"""Support for Amcrest IP cameras."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from functools import partial
|
||||
|
@ -181,7 +183,9 @@ class AmcrestCam(Camera):
|
|||
finally:
|
||||
self._snapshot_task = None
|
||||
|
||||
async def async_camera_image(self):
|
||||
async def async_camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
_LOGGER.debug("Take snapshot from %s", self._name)
|
||||
try:
|
||||
|
|
|
@ -61,7 +61,7 @@ class AmcrestSensor(SensorEntity):
|
|||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
|
@ -76,7 +76,7 @@ class AmcrestSensor(SensorEntity):
|
|||
return self._icon
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
def native_unit_of_measurement(self):
|
||||
"""Return the units of measurement."""
|
||||
return self._unit_of_measurement
|
||||
|
||||
|
|
|
@ -5,12 +5,13 @@ from homeassistant.components import websocket_api
|
|||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.event import async_call_later, async_track_time_interval
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .analytics import Analytics
|
||||
from .const import ATTR_ONBOARDED, ATTR_PREFERENCES, DOMAIN, INTERVAL, PREFERENCE_SCHEMA
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, _):
|
||||
async def async_setup(hass: HomeAssistant, _: ConfigType) -> bool:
|
||||
"""Set up the analytics integration."""
|
||||
analytics = Analytics(hass)
|
||||
|
||||
|
|
|
@ -50,12 +50,12 @@ class IPWebcamSensor(AndroidIPCamEntity, SensorEntity):
|
|||
return self._name
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
def native_unit_of_measurement(self):
|
||||
"""Return the unit the value is expressed in."""
|
||||
return self._unit
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Android TV",
|
||||
"documentation": "https://www.home-assistant.io/integrations/androidtv",
|
||||
"requirements": [
|
||||
"adb-shell[async]==0.3.4",
|
||||
"adb-shell[async]==0.4.0",
|
||||
"androidtv[async]==0.0.60",
|
||||
"pure-python-adb[async]==0.3.0.dev0"
|
||||
],
|
||||
|
|
|
@ -449,6 +449,11 @@ class ADBDevice(MediaPlayerEntity):
|
|||
ATTR_HDMI_INPUT: None,
|
||||
}
|
||||
|
||||
@property
|
||||
def media_image_hash(self):
|
||||
"""Hash value for media image."""
|
||||
return f"{datetime.now().timestamp()}" if self._screencap else None
|
||||
|
||||
@adb_decorator()
|
||||
async def _adb_screencap(self):
|
||||
"""Take a screen capture from the device."""
|
||||
|
@ -458,9 +463,6 @@ class ADBDevice(MediaPlayerEntity):
|
|||
"""Fetch current playing image."""
|
||||
if not self._screencap or self.state in (STATE_OFF, None) or not self.available:
|
||||
return None, None
|
||||
self._attr_media_image_hash = (
|
||||
f"{datetime.now().timestamp()}" if self._screencap else None
|
||||
)
|
||||
|
||||
media_data = await self._adb_screencap()
|
||||
if media_data:
|
||||
|
|
|
@ -165,16 +165,16 @@ class APCUPSdSensor(SensorEntity):
|
|||
self.type = sensor_type
|
||||
self._attr_name = SENSOR_PREFIX + SENSOR_TYPES[sensor_type][0]
|
||||
self._attr_icon = SENSOR_TYPES[self.type][2]
|
||||
self._attr_unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
self._attr_device_class = SENSOR_TYPES[sensor_type][3]
|
||||
|
||||
def update(self):
|
||||
"""Get the latest status and use it to update our sensor state."""
|
||||
if self.type.upper() not in self._data.status:
|
||||
self._attr_state = None
|
||||
self._attr_native_value = None
|
||||
else:
|
||||
self._attr_state, inferred_unit = infer_unit(
|
||||
self._attr_native_value, inferred_unit = infer_unit(
|
||||
self._data.status[self.type.upper()]
|
||||
)
|
||||
if not self._attr_unit_of_measurement:
|
||||
self._attr_unit_of_measurement = inferred_unit
|
||||
if not self._attr_native_unit_of_measurement:
|
||||
self._attr_native_unit_of_measurement = inferred_unit
|
||||
|
|
|
@ -43,7 +43,6 @@ from homeassistant.helpers.system_info import async_get_system_info
|
|||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_BASE_URL = "base_url"
|
||||
ATTR_CURRENCY = "currency"
|
||||
ATTR_EXTERNAL_URL = "external_url"
|
||||
ATTR_INTERNAL_URL = "internal_url"
|
||||
ATTR_LOCATION_NAME = "location_name"
|
||||
|
@ -196,7 +195,6 @@ class APIDiscoveryView(HomeAssistantView):
|
|||
# always needs authentication
|
||||
ATTR_REQUIRES_API_PASSWORD: True,
|
||||
ATTR_VERSION: __version__,
|
||||
ATTR_CURRENCY: None,
|
||||
}
|
||||
|
||||
with suppress(NoURLAvailableError):
|
||||
|
|
|
@ -92,10 +92,11 @@ class AquaLogicSensor(SensorEntity):
|
|||
panel = self._processor.panel
|
||||
if panel is not None:
|
||||
if panel.is_metric:
|
||||
self._attr_unit_of_measurement = SENSOR_TYPES[self._type][1][0]
|
||||
self._attr_state = getattr(panel, self._type)
|
||||
self.async_write_ha_state()
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[self._type][1][0]
|
||||
else:
|
||||
self._attr_unit_of_measurement = SENSOR_TYPES[self._type][1][1]
|
||||
self._attr_native_unit_of_measurement = SENSOR_TYPES[self._type][1][1]
|
||||
|
||||
self._attr_native_value = getattr(panel, self._type)
|
||||
self.async_write_ha_state()
|
||||
else:
|
||||
self._attr_unit_of_measurement = None
|
||||
self._attr_native_unit_of_measurement = None
|
||||
|
|
|
@ -36,7 +36,7 @@ async def _await_cancel(task):
|
|||
await task
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType):
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the component."""
|
||||
hass.data[DOMAIN_DATA_ENTRIES] = {}
|
||||
hass.data[DOMAIN_DATA_TASKS] = {}
|
||||
|
|
|
@ -5,14 +5,27 @@
|
|||
"already_in_progress": "A konfigur\u00e1ci\u00f3 m\u00e1r folyamatban van.",
|
||||
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
|
||||
},
|
||||
"error": {
|
||||
"one": "\u00dcres",
|
||||
"other": "\u00dcres"
|
||||
},
|
||||
"flow_title": "{host}",
|
||||
"step": {
|
||||
"confirm": {
|
||||
"description": "Hozz\u00e1 szeretn\u00e9 adni az Arcam FMJ \"{host}\" eszk\u00f6zt a HomeAssistanthoz?"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Hoszt",
|
||||
"port": "Port"
|
||||
}
|
||||
},
|
||||
"description": "K\u00e9rj\u00fck, adja meg az eszk\u00f6z gazdag\u00e9pnev\u00e9t vagy IP-c\u00edm\u00e9t."
|
||||
}
|
||||
}
|
||||
},
|
||||
"device_automation": {
|
||||
"trigger_type": {
|
||||
"turn_on": "{entity_name} bekapcsol\u00e1s\u00e1t k\u00e9rt\u00e9k"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,4 +42,4 @@ class ArduinoSensor(SensorEntity):
|
|||
|
||||
def update(self):
|
||||
"""Get the latest value from the pin."""
|
||||
self._attr_state = self._board.get_analog_inputs()[self._pin][1]
|
||||
self._attr_native_value = self._board.get_analog_inputs()[self._pin][1]
|
||||
|
|
|
@ -141,7 +141,7 @@ class ArestSensor(SensorEntity):
|
|||
self.arest = arest
|
||||
self._attr_name = f"{location.title()} {name.title()}"
|
||||
self._variable = variable
|
||||
self._attr_unit_of_measurement = unit_of_measurement
|
||||
self._attr_native_unit_of_measurement = unit_of_measurement
|
||||
self._renderer = renderer
|
||||
|
||||
if pin is not None:
|
||||
|
@ -155,9 +155,9 @@ class ArestSensor(SensorEntity):
|
|||
self._attr_available = self.arest.available
|
||||
values = self.arest.data
|
||||
if "error" in values:
|
||||
self._attr_state = values["error"]
|
||||
self._attr_native_value = values["error"]
|
||||
else:
|
||||
self._attr_state = self._renderer(
|
||||
self._attr_native_value = self._renderer(
|
||||
values.get("value", values.get(self._variable, None))
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"""Support for Netgear Arlo IP cameras."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from haffmpeg.camera import CameraMjpeg
|
||||
|
@ -62,7 +64,9 @@ class ArloCam(Camera):
|
|||
self._last_refresh = None
|
||||
self.attrs = {}
|
||||
|
||||
def camera_image(self):
|
||||
def camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
return self._camera.last_image_from_cache
|
||||
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
"""Sensor support for Netgear Arlo IP cameras."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import replace
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
PERCENTAGE,
|
||||
|
@ -22,22 +30,59 @@ from . import ATTRIBUTION, DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# sensor_type [ description, unit, icon ]
|
||||
SENSOR_TYPES = {
|
||||
"last_capture": ["Last", None, "run-fast"],
|
||||
"total_cameras": ["Arlo Cameras", None, "video"],
|
||||
"captured_today": ["Captured Today", None, "file-video"],
|
||||
"battery_level": ["Battery Level", PERCENTAGE, "battery-50"],
|
||||
"signal_strength": ["Signal Strength", None, "signal"],
|
||||
"temperature": ["Temperature", TEMP_CELSIUS, "thermometer"],
|
||||
"humidity": ["Humidity", PERCENTAGE, "water-percent"],
|
||||
"air_quality": ["Air Quality", CONCENTRATION_PARTS_PER_MILLION, "biohazard"],
|
||||
}
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key="last_capture",
|
||||
name="Last",
|
||||
icon="mdi:run-fast",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="total_cameras",
|
||||
name="Arlo Cameras",
|
||||
icon="mdi:video",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="captured_today",
|
||||
name="Captured Today",
|
||||
icon="mdi:file-video",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="battery_level",
|
||||
name="Battery Level",
|
||||
unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="signal_strength",
|
||||
name="Signal Strength",
|
||||
icon="mdi:signal",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="temperature",
|
||||
name="Temperature",
|
||||
unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="humidity",
|
||||
name="Humidity",
|
||||
unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="air_quality",
|
||||
name="Air Quality",
|
||||
unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
icon="mdi:biohazard",
|
||||
),
|
||||
)
|
||||
|
||||
SENSOR_KEYS = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
||||
vol.Required(CONF_MONITORED_CONDITIONS, default=SENSOR_KEYS): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_KEYS)]
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -50,24 +95,27 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
return
|
||||
|
||||
sensors = []
|
||||
for sensor_type in config[CONF_MONITORED_CONDITIONS]:
|
||||
if sensor_type == "total_cameras":
|
||||
sensors.append(ArloSensor(SENSOR_TYPES[sensor_type][0], arlo, sensor_type))
|
||||
for sensor_original in SENSOR_TYPES:
|
||||
if sensor_original.key not in config[CONF_MONITORED_CONDITIONS]:
|
||||
continue
|
||||
sensor_entry = replace(sensor_original)
|
||||
if sensor_entry.key == "total_cameras":
|
||||
sensors.append(ArloSensor(arlo, sensor_entry))
|
||||
else:
|
||||
for camera in arlo.cameras:
|
||||
if sensor_type in ("temperature", "humidity", "air_quality"):
|
||||
if sensor_entry.key in ("temperature", "humidity", "air_quality"):
|
||||
continue
|
||||
|
||||
name = f"{SENSOR_TYPES[sensor_type][0]} {camera.name}"
|
||||
sensors.append(ArloSensor(name, camera, sensor_type))
|
||||
sensor_entry.name = f"{sensor_entry.name} {camera.name}"
|
||||
sensors.append(ArloSensor(camera, sensor_entry))
|
||||
|
||||
for base_station in arlo.base_stations:
|
||||
if (
|
||||
sensor_type in ("temperature", "humidity", "air_quality")
|
||||
sensor_entry.key in ("temperature", "humidity", "air_quality")
|
||||
and base_station.model_id == "ABC1000"
|
||||
):
|
||||
name = f"{SENSOR_TYPES[sensor_type][0]} {base_station.name}"
|
||||
sensors.append(ArloSensor(name, base_station, sensor_type))
|
||||
sensor_entry.name = f"{sensor_entry.name} {base_station.name}"
|
||||
sensors.append(ArloSensor(base_station, sensor_entry))
|
||||
|
||||
add_entities(sensors, True)
|
||||
|
||||
|
@ -75,19 +123,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
class ArloSensor(SensorEntity):
|
||||
"""An implementation of a Netgear Arlo IP sensor."""
|
||||
|
||||
def __init__(self, name, device, sensor_type):
|
||||
def __init__(self, device, sensor_entry):
|
||||
"""Initialize an Arlo sensor."""
|
||||
_LOGGER.debug("ArloSensor created for %s", name)
|
||||
self._name = name
|
||||
self.entity_description = sensor_entry
|
||||
self._data = device
|
||||
self._sensor_type = sensor_type
|
||||
self._state = None
|
||||
self._icon = f"mdi:{SENSOR_TYPES.get(self._sensor_type)[2]}"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of this camera."""
|
||||
return self._name
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
|
@ -103,43 +143,29 @@ class ArloSensor(SensorEntity):
|
|||
self.async_schedule_update_ha_state(True)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Icon to use in the frontend, if any."""
|
||||
if self._sensor_type == "battery_level" and self._state is not None:
|
||||
if self.entity_description.key == "battery_level" and self._state is not None:
|
||||
return icon_for_battery_level(
|
||||
battery_level=int(self._state), charging=False
|
||||
)
|
||||
return self._icon
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the units of measurement."""
|
||||
return SENSOR_TYPES.get(self._sensor_type)[1]
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device class of the sensor."""
|
||||
if self._sensor_type == "temperature":
|
||||
return DEVICE_CLASS_TEMPERATURE
|
||||
if self._sensor_type == "humidity":
|
||||
return DEVICE_CLASS_HUMIDITY
|
||||
return None
|
||||
return self.entity_description.icon
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data and updates the state."""
|
||||
_LOGGER.debug("Updating Arlo sensor %s", self.name)
|
||||
if self._sensor_type == "total_cameras":
|
||||
if self.entity_description.key == "total_cameras":
|
||||
self._state = len(self._data.cameras)
|
||||
|
||||
elif self._sensor_type == "captured_today":
|
||||
elif self.entity_description.key == "captured_today":
|
||||
self._state = len(self._data.captured_today)
|
||||
|
||||
elif self._sensor_type == "last_capture":
|
||||
elif self.entity_description.key == "last_capture":
|
||||
try:
|
||||
video = self._data.last_video
|
||||
self._state = video.created_at_pretty("%m-%d-%Y %H:%M:%S")
|
||||
|
@ -151,31 +177,31 @@ class ArloSensor(SensorEntity):
|
|||
_LOGGER.debug(error_msg)
|
||||
self._state = None
|
||||
|
||||
elif self._sensor_type == "battery_level":
|
||||
elif self.entity_description.key == "battery_level":
|
||||
try:
|
||||
self._state = self._data.battery_level
|
||||
except TypeError:
|
||||
self._state = None
|
||||
|
||||
elif self._sensor_type == "signal_strength":
|
||||
elif self.entity_description.key == "signal_strength":
|
||||
try:
|
||||
self._state = self._data.signal_strength
|
||||
except TypeError:
|
||||
self._state = None
|
||||
|
||||
elif self._sensor_type == "temperature":
|
||||
elif self.entity_description.key == "temperature":
|
||||
try:
|
||||
self._state = self._data.ambient_temperature
|
||||
except TypeError:
|
||||
self._state = None
|
||||
|
||||
elif self._sensor_type == "humidity":
|
||||
elif self.entity_description.key == "humidity":
|
||||
try:
|
||||
self._state = self._data.ambient_humidity
|
||||
except TypeError:
|
||||
self._state = None
|
||||
|
||||
elif self._sensor_type == "air_quality":
|
||||
elif self.entity_description.key == "air_quality":
|
||||
try:
|
||||
self._state = self._data.ambient_air_quality
|
||||
except TypeError:
|
||||
|
@ -189,7 +215,7 @@ class ArloSensor(SensorEntity):
|
|||
attrs[ATTR_ATTRIBUTION] = ATTRIBUTION
|
||||
attrs["brand"] = DEFAULT_BRAND
|
||||
|
||||
if self._sensor_type != "total_cameras":
|
||||
if self.entity_description.key != "total_cameras":
|
||||
attrs["model"] = self._data.model_id
|
||||
|
||||
return attrs
|
||||
|
|
|
@ -138,7 +138,7 @@ class ArwnSensor(SensorEntity):
|
|||
# This mqtt topic for the sensor which is its uid
|
||||
self._attr_unique_id = topic
|
||||
self._state_key = state_key
|
||||
self._attr_unit_of_measurement = units
|
||||
self._attr_native_unit_of_measurement = units
|
||||
self._attr_icon = icon
|
||||
self._attr_device_class = device_class
|
||||
|
||||
|
@ -147,5 +147,5 @@ class ArwnSensor(SensorEntity):
|
|||
ev = {}
|
||||
ev.update(event)
|
||||
self._attr_extra_state_attributes = ev
|
||||
self._attr_state = ev.get(self._state_key, None)
|
||||
self._attr_native_value = ev.get(self._state_key, None)
|
||||
self.async_write_ha_state()
|
||||
|
|
|
@ -60,6 +60,12 @@ class AsusWrtDevice(ScannerEntity):
|
|||
self._device = device
|
||||
self._attr_unique_id = device.mac
|
||||
self._attr_name = device.name or DEFAULT_DEVICE_NAME
|
||||
self._attr_device_info = {
|
||||
"connections": {(CONNECTION_NETWORK_MAC, device.mac)},
|
||||
"default_model": "ASUSWRT Tracked device",
|
||||
}
|
||||
if device.name:
|
||||
self._attr_device_info["default_name"] = device.name
|
||||
|
||||
@property
|
||||
def is_connected(self):
|
||||
|
@ -90,11 +96,6 @@ class AsusWrtDevice(ScannerEntity):
|
|||
def async_on_demand_update(self):
|
||||
"""Update state."""
|
||||
self._device = self._router.devices[self._device.mac]
|
||||
self._attr_device_info = {
|
||||
"connections": {(CONNECTION_NETWORK_MAC, self._device.mac)},
|
||||
}
|
||||
if self._device.name:
|
||||
self._attr_device_info["default_name"] = self._device.name
|
||||
self._attr_extra_state_attributes = {}
|
||||
if self._device.last_activity:
|
||||
self._attr_extra_state_attributes[
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
"""Asuswrt status sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from numbers import Number
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.components.sensor import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import DATA_GIGABYTES, DATA_RATE_MEGABITS_PER_SECOND
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
DataUpdateCoordinator,
|
||||
|
@ -25,62 +29,90 @@ from .const import (
|
|||
)
|
||||
from .router import KEY_COORDINATOR, KEY_SENSORS, AsusWrtRouter
|
||||
|
||||
|
||||
@dataclass
|
||||
class AsusWrtSensorEntityDescription(SensorEntityDescription):
|
||||
"""A class that describes AsusWrt sensor entities."""
|
||||
|
||||
factor: int | None = None
|
||||
precision: int = 2
|
||||
|
||||
|
||||
DEFAULT_PREFIX = "Asuswrt"
|
||||
|
||||
SENSOR_DEVICE_CLASS = "device_class"
|
||||
SENSOR_ICON = "icon"
|
||||
SENSOR_NAME = "name"
|
||||
SENSOR_UNIT = "unit"
|
||||
SENSOR_FACTOR = "factor"
|
||||
SENSOR_DEFAULT_ENABLED = "default_enabled"
|
||||
|
||||
UNIT_DEVICES = "Devices"
|
||||
|
||||
CONNECTION_SENSORS = {
|
||||
SENSORS_CONNECTED_DEVICE[0]: {
|
||||
SENSOR_NAME: "Devices Connected",
|
||||
SENSOR_UNIT: UNIT_DEVICES,
|
||||
SENSOR_FACTOR: 0,
|
||||
SENSOR_ICON: "mdi:router-network",
|
||||
SENSOR_DEFAULT_ENABLED: True,
|
||||
},
|
||||
SENSORS_RATES[0]: {
|
||||
SENSOR_NAME: "Download Speed",
|
||||
SENSOR_UNIT: DATA_RATE_MEGABITS_PER_SECOND,
|
||||
SENSOR_FACTOR: 125000,
|
||||
SENSOR_ICON: "mdi:download-network",
|
||||
},
|
||||
SENSORS_RATES[1]: {
|
||||
SENSOR_NAME: "Upload Speed",
|
||||
SENSOR_UNIT: DATA_RATE_MEGABITS_PER_SECOND,
|
||||
SENSOR_FACTOR: 125000,
|
||||
SENSOR_ICON: "mdi:upload-network",
|
||||
},
|
||||
SENSORS_BYTES[0]: {
|
||||
SENSOR_NAME: "Download",
|
||||
SENSOR_UNIT: DATA_GIGABYTES,
|
||||
SENSOR_FACTOR: 1000000000,
|
||||
SENSOR_ICON: "mdi:download",
|
||||
},
|
||||
SENSORS_BYTES[1]: {
|
||||
SENSOR_NAME: "Upload",
|
||||
SENSOR_UNIT: DATA_GIGABYTES,
|
||||
SENSOR_FACTOR: 1000000000,
|
||||
SENSOR_ICON: "mdi:upload",
|
||||
},
|
||||
SENSORS_LOAD_AVG[0]: {
|
||||
SENSOR_NAME: "Load Avg (1m)",
|
||||
SENSOR_ICON: "mdi:cpu-32-bit",
|
||||
},
|
||||
SENSORS_LOAD_AVG[1]: {
|
||||
SENSOR_NAME: "Load Avg (5m)",
|
||||
SENSOR_ICON: "mdi:cpu-32-bit",
|
||||
},
|
||||
SENSORS_LOAD_AVG[2]: {
|
||||
SENSOR_NAME: "Load Avg (15m)",
|
||||
SENSOR_ICON: "mdi:cpu-32-bit",
|
||||
},
|
||||
}
|
||||
CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = (
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_CONNECTED_DEVICE[0],
|
||||
name="Devices Connected",
|
||||
icon="mdi:router-network",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
native_unit_of_measurement=UNIT_DEVICES,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_RATES[0],
|
||||
name="Download Speed",
|
||||
icon="mdi:download-network",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=125000,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_RATES[1],
|
||||
name="Upload Speed",
|
||||
icon="mdi:upload-network",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=125000,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_BYTES[0],
|
||||
name="Download",
|
||||
icon="mdi:download",
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
native_unit_of_measurement=DATA_GIGABYTES,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=1000000000,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_BYTES[1],
|
||||
name="Upload",
|
||||
icon="mdi:upload",
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
native_unit_of_measurement=DATA_GIGABYTES,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=1000000000,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_LOAD_AVG[0],
|
||||
name="Load Avg (1m)",
|
||||
icon="mdi:cpu-32-bit",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=1,
|
||||
precision=1,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_LOAD_AVG[1],
|
||||
name="Load Avg (5m)",
|
||||
icon="mdi:cpu-32-bit",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=1,
|
||||
precision=1,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_LOAD_AVG[2],
|
||||
name="Load Avg (15m)",
|
||||
icon="mdi:cpu-32-bit",
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=1,
|
||||
precision=1,
|
||||
),
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -95,13 +127,13 @@ async def async_setup_entry(
|
|||
for sensor_data in router.sensors_coordinator.values():
|
||||
coordinator = sensor_data[KEY_COORDINATOR]
|
||||
sensors = sensor_data[KEY_SENSORS]
|
||||
for sensor_key in sensors:
|
||||
if sensor_key in CONNECTION_SENSORS:
|
||||
entities.append(
|
||||
AsusWrtSensor(
|
||||
coordinator, router, sensor_key, CONNECTION_SENSORS[sensor_key]
|
||||
)
|
||||
)
|
||||
entities.extend(
|
||||
[
|
||||
AsusWrtSensor(coordinator, router, sensor_descr)
|
||||
for sensor_descr in CONNECTION_SENSORS
|
||||
if sensor_descr.key in sensors
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
@ -113,39 +145,23 @@ class AsusWrtSensor(CoordinatorEntity, SensorEntity):
|
|||
self,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
router: AsusWrtRouter,
|
||||
sensor_type: str,
|
||||
sensor_def: dict[str, Any],
|
||||
description: AsusWrtSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize a AsusWrt sensor."""
|
||||
super().__init__(coordinator)
|
||||
self._router = router
|
||||
self._sensor_type = sensor_type
|
||||
self._attr_name = f"{DEFAULT_PREFIX} {sensor_def[SENSOR_NAME]}"
|
||||
self._factor = sensor_def.get(SENSOR_FACTOR)
|
||||
self.entity_description = description
|
||||
|
||||
self._attr_name = f"{DEFAULT_PREFIX} {description.name}"
|
||||
self._attr_unique_id = f"{DOMAIN} {self.name}"
|
||||
self._attr_entity_registry_enabled_default = sensor_def.get(
|
||||
SENSOR_DEFAULT_ENABLED, False
|
||||
)
|
||||
self._attr_unit_of_measurement = sensor_def.get(SENSOR_UNIT)
|
||||
self._attr_icon = sensor_def.get(SENSOR_ICON)
|
||||
self._attr_device_class = sensor_def.get(SENSOR_DEVICE_CLASS)
|
||||
self._attr_device_info = router.device_info
|
||||
self._attr_extra_state_attributes = {"hostname": router.host}
|
||||
|
||||
@property
|
||||
def state(self) -> str:
|
||||
def native_value(self) -> str | None:
|
||||
"""Return current state."""
|
||||
state = self.coordinator.data.get(self._sensor_type)
|
||||
if state is None:
|
||||
return None
|
||||
if self._factor and isinstance(state, Number):
|
||||
return round(state / self._factor, 2)
|
||||
descr = self.entity_description
|
||||
state = self.coordinator.data.get(descr.key)
|
||||
if state is not None:
|
||||
if descr.factor and isinstance(state, Number):
|
||||
return round(state / descr.factor, descr.precision)
|
||||
return state
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the attributes."""
|
||||
return {"hostname": self._router.host}
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device information."""
|
||||
return self._router.device_info
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"single_instance_allowed": "\u5df2\u7ecf\u914d\u7f6e\u8fc7\u4e86\uff0c\u4e14\u53ea\u80fd\u914d\u7f6e\u4e00\u6b21\u3002"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u8fde\u63a5\u5931\u8d25",
|
||||
"invalid_host": "\u65e0\u6548\u7684\u4e3b\u673a\u5730\u5740\u6216 IP \u5730\u5740",
|
||||
"pwd_and_ssh": "\u53ea\u63d0\u4f9b\u5bc6\u7801\u6216 SSH \u5bc6\u94a5\u6587\u4ef6",
|
||||
"pwd_or_ssh": "\u8bf7\u63d0\u4f9b\u5bc6\u7801\u6216 SSH \u5bc6\u94a5\u6587\u4ef6",
|
||||
"ssh_not_file": "\u672a\u627e\u5230 SSH \u5bc6\u94a5\u6587\u4ef6",
|
||||
"unknown": "\u672a\u77e5\u9519\u8bef"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u4e3b\u673a\u5730\u5740",
|
||||
"mode": "\u4f7f\u7528\u6a21\u5f0f",
|
||||
"name": "\u540d\u79f0",
|
||||
"password": "\u5bc6\u7801",
|
||||
"port": "\u7aef\u53e3",
|
||||
"protocol": "\u901a\u4fe1\u534f\u8bae",
|
||||
"ssh_key": "SSH \u5bc6\u94a5\u6587\u4ef6\u8def\u5f84 (\u4e0d\u662f\u5bc6\u7801)",
|
||||
"username": "\u7528\u6237\u540d"
|
||||
},
|
||||
"description": "\u8bbe\u7f6e\u8fde\u63a5\u5230\u8def\u7531\u5668\u6240\u9700\u7684\u53c2\u6570",
|
||||
"title": "AsusWRT"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"consider_home": "\u7b49\u5f85\u591a\u5c11\u79d2\u540e\u5219\u5224\u5b9a\u8bbe\u5907\u79bb\u5f00",
|
||||
"dnsmasq": "\u8def\u7531\u5668\u4e2d\u7684 dnsmasq.leases \u6587\u4ef6\u4f4d\u7f6e",
|
||||
"interface": "\u60f3\u8981\u76d1\u6d4b\u7684\u7aef\u53e3(\u4f8b\u5982: eth0,eth1 \u7b49)",
|
||||
"require_ip": "\u8bbe\u5907\u5fc5\u987b\u5177\u6709 IP (\u7528\u4e8e\u63a5\u5165\u70b9\u6a21\u5f0f)"
|
||||
},
|
||||
"title": "AsusWRT \u9009\u9879"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,10 +49,12 @@ class AtagSensor(AtagEntity, SensorEntity):
|
|||
PERCENTAGE,
|
||||
TIME_HOURS,
|
||||
):
|
||||
self._attr_unit_of_measurement = coordinator.data.report[self._id].measure
|
||||
self._attr_native_unit_of_measurement = coordinator.data.report[
|
||||
self._id
|
||||
].measure
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def native_value(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self.coordinator.data.report[self._id].state
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue