forked from home-assistant/core
Compare commits
91 Commits
2023.4.0b2
...
2023.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
| cff493fb98 | |||
| d67265bb66 | |||
| 6e51f0d6f5 | |||
| 82977f33ed | |||
| fb2d432d32 | |||
| 0d019a3c4c | |||
| 65b877bb77 | |||
| 2a23583d67 | |||
| 80fe5051b3 | |||
| 2dfe33d177 | |||
| 617037a92d | |||
| 8f60a2bdd4 | |||
| 9f7b2ba6c1 | |||
| af34e25c89 | |||
| c43dc37713 | |||
| 0d6177dbdb | |||
| f03b9036c5 | |||
| 1848a723cd | |||
| 8230a52e0a | |||
| d0e9470c7c | |||
| b50354f362 | |||
| e4b3a146be | |||
| 1861a621b2 | |||
| 0746e09256 | |||
| 0166cd082b | |||
| 0a74f946db | |||
| d04b45a821 | |||
| a5a6641bb4 | |||
| 1420cda837 | |||
| cba5751ca2 | |||
| a3e66b5dde | |||
| 83dd52ab1f | |||
| da1e5f6a3c | |||
| 8f9868024c | |||
| c90396cd57 | |||
| 509c1ca99c | |||
| 431fbee641 | |||
| 28983bca85 | |||
| 601498617d | |||
| 6c208f655d | |||
| eaaf24d326 | |||
| 0c12d45581 | |||
| c2e46db76d | |||
| 47c8b7804d | |||
| 8d302aea9e | |||
| 3a73425888 | |||
| f9e4fe016f | |||
| 5835ae03bc | |||
| 71608d4795 | |||
| e38590e40a | |||
| 9e3b54f539 | |||
| 24ff2ddae5 | |||
| 621de8bb5f | |||
| 6cbf9288b5 | |||
| 9f95da7793 | |||
| cb5326b798 | |||
| 1aa6d3e896 | |||
| 1c8d4b8bb8 | |||
| 8669ee3685 | |||
| 20d8bbbd0c | |||
| e10e3ee7cc | |||
| 83b7018be2 | |||
| 6d967ac535 | |||
| 77bc745bed | |||
| 8fe7b01baa | |||
| 5e5888b37a | |||
| 90de51fff3 | |||
| 89230b75be | |||
| cbe3cabf0a | |||
| c259c1afe3 | |||
| 1ff93518b5 | |||
| aa6cf3d208 | |||
| 2a28d40dc8 | |||
| c006b3b1df | |||
| bacd77a03a | |||
| 75694307e2 | |||
| 1189b2ad70 | |||
| d5d5bb0732 | |||
| 6242dd2214 | |||
| 03f085d7be | |||
| b3348c3e6f | |||
| 590db0fa74 | |||
| f56ccf90d9 | |||
| c63f8e714e | |||
| a20771f571 | |||
| 2d482f1f57 | |||
| 499962f4ee | |||
| 88a407361c | |||
| 89dc6db5a7 | |||
| de9e7e47fe | |||
| ab66664f20 |
@@ -228,8 +228,6 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/cups/ @fabaff
|
||||
/homeassistant/components/daikin/ @fredrike
|
||||
/tests/components/daikin/ @fredrike
|
||||
/homeassistant/components/darksky/ @fabaff
|
||||
/tests/components/darksky/ @fabaff
|
||||
/homeassistant/components/debugpy/ @frenck
|
||||
/tests/components/debugpy/ @frenck
|
||||
/homeassistant/components/deconz/ @Kane610
|
||||
|
||||
@@ -239,6 +239,7 @@ async def load_registries(hass: core.HomeAssistant) -> None:
|
||||
|
||||
# Load the registries and cache the result of platform.uname().processor
|
||||
entity.async_setup(hass)
|
||||
template.async_setup(hass)
|
||||
await asyncio.gather(
|
||||
area_registry.async_load(hass),
|
||||
device_registry.async_load(hass),
|
||||
|
||||
@@ -68,7 +68,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_CAQI,
|
||||
icon="mdi:air-filter",
|
||||
name=ATTR_API_CAQI,
|
||||
translation_key="caqi",
|
||||
native_unit_of_measurement="CAQI",
|
||||
suggested_display_precision=0,
|
||||
attrs=lambda data: {
|
||||
@@ -80,7 +80,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM1,
|
||||
device_class=SensorDeviceClass.PM1,
|
||||
name="PM1.0",
|
||||
translation_key="pm1",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
@@ -88,7 +88,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM25,
|
||||
device_class=SensorDeviceClass.PM25,
|
||||
name="PM2.5",
|
||||
translation_key="pm25",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
@@ -100,7 +100,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PM10,
|
||||
device_class=SensorDeviceClass.PM10,
|
||||
name=ATTR_API_PM10,
|
||||
translation_key="pm10",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
@@ -112,7 +112,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_HUMIDITY,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
name=ATTR_API_HUMIDITY.capitalize(),
|
||||
translation_key="humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=1,
|
||||
@@ -120,7 +120,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_PRESSURE,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
name=ATTR_API_PRESSURE.capitalize(),
|
||||
translation_key="pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.HPA,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
@@ -128,14 +128,14 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
name=ATTR_API_TEMPERATURE.capitalize(),
|
||||
translation_key="temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=1,
|
||||
),
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_CO,
|
||||
name="Carbon monoxide",
|
||||
translation_key="co",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
@@ -147,7 +147,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_NO2,
|
||||
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
|
||||
name="Nitrogen dioxide",
|
||||
translation_key="no2",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
@@ -159,7 +159,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_SO2,
|
||||
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
|
||||
name="Sulphur dioxide",
|
||||
translation_key="so2",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
@@ -171,7 +171,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
|
||||
AirlySensorEntityDescription(
|
||||
key=ATTR_API_O3,
|
||||
device_class=SensorDeviceClass.OZONE,
|
||||
name="Ozone",
|
||||
translation_key="o3",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
|
||||
@@ -26,5 +26,42 @@
|
||||
"requests_remaining": "Remaining allowed requests",
|
||||
"requests_per_day": "Allowed requests per day"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"caqi": {
|
||||
"name": "Common air quality index"
|
||||
},
|
||||
"pm1": {
|
||||
"name": "[%key:component::sensor::entity_component::pm1::name%]"
|
||||
},
|
||||
"pm25": {
|
||||
"name": "[%key:component::sensor::entity_component::pm25::name%]"
|
||||
},
|
||||
"pm10": {
|
||||
"name": "[%key:component::sensor::entity_component::pm10::name%]"
|
||||
},
|
||||
"humidity": {
|
||||
"name": "[%key:component::sensor::entity_component::humidity::name%]"
|
||||
},
|
||||
"pressure": {
|
||||
"name": "[%key:component::sensor::entity_component::pressure::name%]"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"co": {
|
||||
"name": "[%key:component::sensor::entity_component::carbon_monoxide::name%]"
|
||||
},
|
||||
"no2": {
|
||||
"name": "[%key:component::sensor::entity_component::nitrogen_dioxide::name%]"
|
||||
},
|
||||
"so2": {
|
||||
"name": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]"
|
||||
},
|
||||
"o3": {
|
||||
"name": "[%key:component::sensor::entity_component::ozone::name%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ async def async_setup(hass: HomeAssistant, _: ConfigType) -> bool:
|
||||
|
||||
# Send every day
|
||||
async_track_time_interval(
|
||||
hass, analytics.send_analytics, INTERVAL, "analytics daily"
|
||||
hass, analytics.send_analytics, INTERVAL, name="analytics daily"
|
||||
)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, start_schedule)
|
||||
|
||||
@@ -38,7 +38,10 @@ class AugustSubscriberMixin:
|
||||
def _async_setup_listeners(self):
|
||||
"""Create interval and stop listeners."""
|
||||
self._unsub_interval = async_track_time_interval(
|
||||
self._hass, self._async_refresh, self._update_interval, "august refresh"
|
||||
self._hass,
|
||||
self._async_refresh,
|
||||
self._update_interval,
|
||||
name="august refresh",
|
||||
)
|
||||
|
||||
@callback
|
||||
|
||||
@@ -101,7 +101,7 @@ class BaseHaScanner(ABC):
|
||||
self.hass,
|
||||
self._async_scanner_watchdog,
|
||||
SCANNER_WATCHDOG_INTERVAL,
|
||||
f"{self.name} Bluetooth scanner watchdog",
|
||||
name=f"{self.name} Bluetooth scanner watchdog",
|
||||
)
|
||||
|
||||
@hass_callback
|
||||
@@ -230,7 +230,7 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
||||
self.hass,
|
||||
self._async_expire_devices,
|
||||
timedelta(seconds=30),
|
||||
f"{self.name} Bluetooth scanner device expire",
|
||||
name=f"{self.name} Bluetooth scanner device expire",
|
||||
)
|
||||
cancel_stop = self.hass.bus.async_listen(
|
||||
EVENT_HOMEASSISTANT_STOP, self._async_save_history
|
||||
@@ -345,12 +345,27 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
||||
tx_power=NO_RSSI_VALUE if tx_power is None else tx_power,
|
||||
platform_data=(),
|
||||
)
|
||||
device = BLEDevice(
|
||||
address=address,
|
||||
name=local_name,
|
||||
details=self._details | details,
|
||||
rssi=rssi, # deprecated, will be removed in newer bleak
|
||||
)
|
||||
if prev_discovery:
|
||||
#
|
||||
# Bleak updates the BLEDevice via create_or_update_device.
|
||||
# We need to do the same to ensure integrations that already
|
||||
# have the BLEDevice object get the updated details when they
|
||||
# change.
|
||||
#
|
||||
# https://github.com/hbldh/bleak/blob/222618b7747f0467dbb32bd3679f8cfaa19b1668/bleak/backends/scanner.py#L203
|
||||
#
|
||||
device = prev_device
|
||||
device.name = local_name
|
||||
device.details = self._details | details
|
||||
# pylint: disable-next=protected-access
|
||||
device._rssi = rssi # deprecated, will be removed in newer bleak
|
||||
else:
|
||||
device = BLEDevice(
|
||||
address=address,
|
||||
name=local_name,
|
||||
details=self._details | details,
|
||||
rssi=rssi, # deprecated, will be removed in newer bleak
|
||||
)
|
||||
self._discovered_device_advertisement_datas[address] = (
|
||||
device,
|
||||
advertisement_data,
|
||||
|
||||
@@ -276,7 +276,7 @@ class BluetoothManager:
|
||||
self.hass,
|
||||
self._async_check_unavailable,
|
||||
timedelta(seconds=UNAVAILABLE_TRACK_SECONDS),
|
||||
"Bluetooth manager unavailable tracking",
|
||||
name="Bluetooth manager unavailable tracking",
|
||||
)
|
||||
|
||||
@hass_callback
|
||||
|
||||
@@ -70,6 +70,7 @@ async def async_setup_scanner( # noqa: C901
|
||||
yaml_path = hass.config.path(YAML_DEVICES)
|
||||
devs_to_track: set[str] = set()
|
||||
devs_no_track: set[str] = set()
|
||||
devs_advertise_time: dict[str, float] = {}
|
||||
devs_track_battery = {}
|
||||
interval: timedelta = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
|
||||
# if track new devices is true discover new devices
|
||||
@@ -178,6 +179,7 @@ async def async_setup_scanner( # noqa: C901
|
||||
"""Update from a ble callback."""
|
||||
mac = service_info.address
|
||||
if mac in devs_to_track:
|
||||
devs_advertise_time[mac] = service_info.time
|
||||
now = dt_util.utcnow()
|
||||
hass.async_create_task(async_see_device(mac, service_info.name))
|
||||
if (
|
||||
@@ -205,7 +207,9 @@ async def async_setup_scanner( # noqa: C901
|
||||
# there have been no callbacks because the RSSI or
|
||||
# other properties have not changed.
|
||||
for service_info in bluetooth.async_discovered_service_info(hass, False):
|
||||
_async_update_ble(service_info, bluetooth.BluetoothChange.ADVERTISEMENT)
|
||||
# Only call _async_update_ble if the advertisement time has changed
|
||||
if service_info.time != devs_advertise_time.get(service_info.address):
|
||||
_async_update_ble(service_info, bluetooth.BluetoothChange.ADVERTISEMENT)
|
||||
|
||||
cancels = [
|
||||
bluetooth.async_register_callback(
|
||||
|
||||
@@ -177,7 +177,7 @@ class BondEntity(Entity):
|
||||
self.hass,
|
||||
self._async_update_if_bpup_not_alive,
|
||||
_FALLBACK_SCAN_INTERVAL,
|
||||
f"Bond {self.entity_id} fallback polling",
|
||||
name=f"Bond {self.entity_id} fallback polling",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -36,14 +36,14 @@ class BraviaTVButtonDescription(
|
||||
BUTTONS: tuple[BraviaTVButtonDescription, ...] = (
|
||||
BraviaTVButtonDescription(
|
||||
key="reboot",
|
||||
name="Reboot",
|
||||
translation_key="restart",
|
||||
device_class=ButtonDeviceClass.RESTART,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_action=lambda coordinator: coordinator.async_reboot_device(),
|
||||
),
|
||||
BraviaTVButtonDescription(
|
||||
key="terminate_apps",
|
||||
name="Terminate apps",
|
||||
translation_key="terminate_apps",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_action=lambda coordinator: coordinator.async_terminate_apps(),
|
||||
),
|
||||
|
||||
@@ -44,5 +44,15 @@
|
||||
"not_bravia_device": "The device is not a Bravia TV.",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"button": {
|
||||
"restart": {
|
||||
"name": "[%key:component::button::entity_component::restart::name%]"
|
||||
},
|
||||
"terminate_apps": {
|
||||
"name": "Terminate apps"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,61 +25,61 @@ from .entity import BroadlinkEntity
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key="temperature",
|
||||
name="Temperature",
|
||||
translation_key="temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="air_quality",
|
||||
name="Air quality",
|
||||
translation_key="air_quality",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="humidity",
|
||||
name="Humidity",
|
||||
translation_key="humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="light",
|
||||
name="Light",
|
||||
translation_key="light",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="noise",
|
||||
name="Noise",
|
||||
translation_key="noise",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="power",
|
||||
name="Current power",
|
||||
translation_key="power",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="volt",
|
||||
name="Voltage",
|
||||
translation_key="voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="current",
|
||||
name="Current",
|
||||
translation_key="current",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="overload",
|
||||
name="Overload",
|
||||
translation_key="overload",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="totalconsum",
|
||||
name="Total consumption",
|
||||
translation_key="total_consumption",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
|
||||
@@ -43,5 +43,39 @@
|
||||
"invalid_host": "[%key:common::config_flow::error::invalid_host%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"temperature": {
|
||||
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"air_quality": {
|
||||
"name": "[%key:component::sensor::entity_component::aqi::name%]"
|
||||
},
|
||||
"humidity": {
|
||||
"name": "[%key:component::sensor::entity_component::humidity::name%]"
|
||||
},
|
||||
"light": {
|
||||
"name": "[%key:component::sensor::entity_component::illuminance::name%]"
|
||||
},
|
||||
"noise": {
|
||||
"name": "Noise"
|
||||
},
|
||||
"power": {
|
||||
"name": "[%key:component::sensor::entity_component::power::name%]"
|
||||
},
|
||||
"voltage": {
|
||||
"name": "[%key:component::sensor::entity_component::voltage::name%]"
|
||||
},
|
||||
"current": {
|
||||
"name": "[%key:component::sensor::entity_component::current::name%]"
|
||||
},
|
||||
"overload": {
|
||||
"name": "Overload"
|
||||
},
|
||||
"total_consumption": {
|
||||
"name": "Total consumption"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,14 +53,14 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="status",
|
||||
icon="mdi:printer",
|
||||
name="Status",
|
||||
translation_key="status",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value=lambda data: data.status,
|
||||
),
|
||||
BrotherSensorEntityDescription(
|
||||
key="page_counter",
|
||||
icon="mdi:file-document-outline",
|
||||
name="Page counter",
|
||||
translation_key="page_counter",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -69,7 +69,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="bw_counter",
|
||||
icon="mdi:file-document-outline",
|
||||
name="B/W counter",
|
||||
translation_key="bw_pages",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -78,7 +78,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="color_counter",
|
||||
icon="mdi:file-document-outline",
|
||||
name="Color counter",
|
||||
translation_key="color_pages",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -87,7 +87,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="duplex_unit_pages_counter",
|
||||
icon="mdi:file-document-outline",
|
||||
name="Duplex unit pages counter",
|
||||
translation_key="duplex_unit_page_counter",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -96,7 +96,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="drum_remaining_life",
|
||||
icon="mdi:chart-donut",
|
||||
name="Drum remaining life",
|
||||
translation_key="drum_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -105,7 +105,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="drum_remaining_pages",
|
||||
icon="mdi:chart-donut",
|
||||
name="Drum remaining pages",
|
||||
translation_key="drum_remaining_pages",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -114,7 +114,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="drum_counter",
|
||||
icon="mdi:chart-donut",
|
||||
name="Drum counter",
|
||||
translation_key="drum_page_counter",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -123,7 +123,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="black_drum_remaining_life",
|
||||
icon="mdi:chart-donut",
|
||||
name="Black drum remaining life",
|
||||
translation_key="black_drum_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -132,7 +132,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="black_drum_remaining_pages",
|
||||
icon="mdi:chart-donut",
|
||||
name="Black drum remaining pages",
|
||||
translation_key="black_drum_remaining_pages",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -141,7 +141,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="black_drum_counter",
|
||||
icon="mdi:chart-donut",
|
||||
name="Black drum counter",
|
||||
translation_key="black_drum_page_counter",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -150,7 +150,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="cyan_drum_remaining_life",
|
||||
icon="mdi:chart-donut",
|
||||
name="Cyan drum remaining life",
|
||||
translation_key="cyan_drum_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -159,7 +159,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="cyan_drum_remaining_pages",
|
||||
icon="mdi:chart-donut",
|
||||
name="Cyan drum remaining pages",
|
||||
translation_key="cyan_drum_remaining_pages",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -168,7 +168,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="cyan_drum_counter",
|
||||
icon="mdi:chart-donut",
|
||||
name="Cyan drum counter",
|
||||
translation_key="cyan_drum_page_counter",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -177,7 +177,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="magenta_drum_remaining_life",
|
||||
icon="mdi:chart-donut",
|
||||
name="Magenta drum remaining life",
|
||||
translation_key="magenta_drum_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -186,7 +186,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="magenta_drum_remaining_pages",
|
||||
icon="mdi:chart-donut",
|
||||
name="Magenta drum remaining pages",
|
||||
translation_key="magenta_drum_remaining_pages",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -195,7 +195,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="magenta_drum_counter",
|
||||
icon="mdi:chart-donut",
|
||||
name="Magenta drum counter",
|
||||
translation_key="magenta_drum_page_counter",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -204,7 +204,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="yellow_drum_remaining_life",
|
||||
icon="mdi:chart-donut",
|
||||
name="Yellow drum remaining life",
|
||||
translation_key="yellow_drum_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -213,7 +213,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="yellow_drum_remaining_pages",
|
||||
icon="mdi:chart-donut",
|
||||
name="Yellow drum remaining pages",
|
||||
translation_key="yellow_drum_remaining_pages",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -222,7 +222,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="yellow_drum_counter",
|
||||
icon="mdi:chart-donut",
|
||||
name="Yellow drum counter",
|
||||
translation_key="yellow_drum_page_counter",
|
||||
native_unit_of_measurement=UNIT_PAGES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -231,7 +231,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="belt_unit_remaining_life",
|
||||
icon="mdi:current-ac",
|
||||
name="Belt unit remaining life",
|
||||
translation_key="belt_unit_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -240,7 +240,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="fuser_remaining_life",
|
||||
icon="mdi:water-outline",
|
||||
name="Fuser remaining life",
|
||||
translation_key="fuser_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -249,7 +249,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="laser_remaining_life",
|
||||
icon="mdi:spotlight-beam",
|
||||
name="Laser remaining life",
|
||||
translation_key="laser_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -258,7 +258,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="pf_kit_1_remaining_life",
|
||||
icon="mdi:printer-3d",
|
||||
name="PF Kit 1 remaining life",
|
||||
translation_key="pf_kit_1_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -267,7 +267,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="pf_kit_mp_remaining_life",
|
||||
icon="mdi:printer-3d",
|
||||
name="PF Kit MP remaining life",
|
||||
translation_key="pf_kit_mp_remaining_life",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -276,7 +276,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="black_toner_remaining",
|
||||
icon="mdi:printer-3d-nozzle",
|
||||
name="Black toner remaining",
|
||||
translation_key="black_toner_remaining",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -285,7 +285,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="cyan_toner_remaining",
|
||||
icon="mdi:printer-3d-nozzle",
|
||||
name="Cyan toner remaining",
|
||||
translation_key="cyan_toner_remaining",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -294,7 +294,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="magenta_toner_remaining",
|
||||
icon="mdi:printer-3d-nozzle",
|
||||
name="Magenta toner remaining",
|
||||
translation_key="magenta_toner_remaining",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -303,7 +303,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="yellow_toner_remaining",
|
||||
icon="mdi:printer-3d-nozzle",
|
||||
name="Yellow toner remaining",
|
||||
translation_key="yellow_toner_remaining",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -312,7 +312,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="black_ink_remaining",
|
||||
icon="mdi:printer-3d-nozzle",
|
||||
name="Black ink remaining",
|
||||
translation_key="black_ink_remaining",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -321,7 +321,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="cyan_ink_remaining",
|
||||
icon="mdi:printer-3d-nozzle",
|
||||
name="Cyan ink remaining",
|
||||
translation_key="cyan_ink_remaining",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -330,7 +330,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="magenta_ink_remaining",
|
||||
icon="mdi:printer-3d-nozzle",
|
||||
name="Magenta ink remaining",
|
||||
translation_key="magenta_ink_remaining",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -339,7 +339,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
BrotherSensorEntityDescription(
|
||||
key="yellow_ink_remaining",
|
||||
icon="mdi:printer-3d-nozzle",
|
||||
name="Yellow ink remaining",
|
||||
translation_key="yellow_ink_remaining",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -347,7 +347,7 @@ SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
|
||||
),
|
||||
BrotherSensorEntityDescription(
|
||||
key="uptime",
|
||||
name="Uptime",
|
||||
translation_key="last_restart",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
|
||||
@@ -25,5 +25,111 @@
|
||||
"unsupported_model": "This printer model is not supported.",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"status": {
|
||||
"name": "Status"
|
||||
},
|
||||
"page_counter": {
|
||||
"name": "Page counter"
|
||||
},
|
||||
"bw_pages": {
|
||||
"name": "B/W pages"
|
||||
},
|
||||
"color_pages": {
|
||||
"name": "Color pages"
|
||||
},
|
||||
"duplex_unit_page_counter": {
|
||||
"name": "Duplex unit page counter"
|
||||
},
|
||||
"drum_remaining_life": {
|
||||
"name": "Drum remaining life"
|
||||
},
|
||||
"drum_remaining_pages": {
|
||||
"name": "Drum remaining pages"
|
||||
},
|
||||
"drum_page_counter": {
|
||||
"name": "Drum page counter"
|
||||
},
|
||||
"black_drum_remaining_life": {
|
||||
"name": "Black drum remaining life"
|
||||
},
|
||||
"black_drum_remaining_pages": {
|
||||
"name": "Black drum remaining pages"
|
||||
},
|
||||
"black_drum_page_counter": {
|
||||
"name": "Black drum page counter"
|
||||
},
|
||||
"cyan_drum_remaining_life": {
|
||||
"name": "Cyan drum remaining life"
|
||||
},
|
||||
"cyan_drum_remaining_pages": {
|
||||
"name": "Cyan drum remaining pages"
|
||||
},
|
||||
"cyan_drum_page_counter": {
|
||||
"name": "Cyan drum page counter"
|
||||
},
|
||||
"magenta_drum_remaining_life": {
|
||||
"name": "Magenta drum remaining life"
|
||||
},
|
||||
"magenta_drum_remaining_pages": {
|
||||
"name": "Magenta drum remaining pages"
|
||||
},
|
||||
"magenta_drum_page_counter": {
|
||||
"name": "Magenta drum page counter"
|
||||
},
|
||||
"yellow_drum_remaining_life": {
|
||||
"name": "Yellow drum remaining life"
|
||||
},
|
||||
"yellow_drum_remaining_pages": {
|
||||
"name": "Yellow drum remaining pages"
|
||||
},
|
||||
"yellow_drum_page_counter": {
|
||||
"name": "Yellow drum page counter"
|
||||
},
|
||||
"belt_unit_remaining_life": {
|
||||
"name": "Belt unit remaining life"
|
||||
},
|
||||
"fuser_remaining_life": {
|
||||
"name": "Fuser remaining life"
|
||||
},
|
||||
"laser_remaining_life": {
|
||||
"name": "Laser remaining life"
|
||||
},
|
||||
"pf_kit_1_remaining_life": {
|
||||
"name": "PF Kit 1 remaining life"
|
||||
},
|
||||
"pf_kit_mp_remaining_life": {
|
||||
"name": "PF Kit MP remaining life"
|
||||
},
|
||||
"black_toner_remaining": {
|
||||
"name": "Black toner remaining"
|
||||
},
|
||||
"cyan_toner_remaining": {
|
||||
"name": "Cyan toner remaining"
|
||||
},
|
||||
"magenta_toner_remaining": {
|
||||
"name": "Magenta toner remaining"
|
||||
},
|
||||
"yellow_toner_remaining": {
|
||||
"name": "Yellow toner remaining"
|
||||
},
|
||||
"black_ink_remaining": {
|
||||
"name": "Black ink remaining"
|
||||
},
|
||||
"cyan_ink_remaining": {
|
||||
"name": "Cyan ink remaining"
|
||||
},
|
||||
"magenta_ink_remaining": {
|
||||
"name": "Magenta ink remaining"
|
||||
},
|
||||
"yellow_ink_remaining": {
|
||||
"name": "Yellow ink remaining"
|
||||
},
|
||||
"last_restart": {
|
||||
"name": "Last restart"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +380,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
entity.async_write_ha_state()
|
||||
|
||||
unsub = async_track_time_interval(
|
||||
hass, update_tokens, TOKEN_CHANGE_INTERVAL, "Camera update tokens"
|
||||
hass, update_tokens, TOKEN_CHANGE_INTERVAL, name="Camera update tokens"
|
||||
)
|
||||
|
||||
@callback
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/cast",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["casttube", "pychromecast"],
|
||||
"requirements": ["pychromecast==13.0.6"],
|
||||
"requirements": ["pychromecast==13.0.7"],
|
||||
"zeroconf": ["_googlecast._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
"""The darksky component."""
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"domain": "darksky",
|
||||
"name": "Dark Sky",
|
||||
"codeowners": ["@fabaff"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/darksky",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["forecastio"],
|
||||
"requirements": ["python-forecastio==1.4.0"]
|
||||
}
|
||||
@@ -1,927 +0,0 @@
|
||||
"""Support for Dark Sky weather service."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Literal, NamedTuple
|
||||
|
||||
import forecastio
|
||||
from requests.exceptions import ConnectionError as ConnectError, HTTPError, Timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA,
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
CONF_NAME,
|
||||
CONF_SCAN_INTERVAL,
|
||||
DEGREE,
|
||||
PERCENTAGE,
|
||||
UV_INDEX,
|
||||
UnitOfLength,
|
||||
UnitOfPrecipitationDepth,
|
||||
UnitOfPressure,
|
||||
UnitOfSpeed,
|
||||
UnitOfTemperature,
|
||||
UnitOfVolumetricFlux,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_FORECAST = "forecast"
|
||||
CONF_HOURLY_FORECAST = "hourly_forecast"
|
||||
CONF_LANGUAGE = "language"
|
||||
CONF_UNITS = "units"
|
||||
|
||||
DEFAULT_LANGUAGE = "en"
|
||||
DEFAULT_NAME = "Dark Sky"
|
||||
SCAN_INTERVAL = timedelta(seconds=300)
|
||||
|
||||
DEPRECATED_SENSOR_TYPES = {
|
||||
"apparent_temperature_max",
|
||||
"apparent_temperature_min",
|
||||
"temperature_max",
|
||||
"temperature_min",
|
||||
}
|
||||
|
||||
MAP_UNIT_SYSTEM: dict[
|
||||
Literal["si", "us", "ca", "uk", "uk2"],
|
||||
Literal["si_unit", "us_unit", "ca_unit", "uk_unit", "uk2_unit"],
|
||||
] = {
|
||||
"si": "si_unit",
|
||||
"us": "us_unit",
|
||||
"ca": "ca_unit",
|
||||
"uk": "uk_unit",
|
||||
"uk2": "uk2_unit",
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class DarkskySensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes Darksky sensor entity."""
|
||||
|
||||
si_unit: str | None = None
|
||||
us_unit: str | None = None
|
||||
ca_unit: str | None = None
|
||||
uk_unit: str | None = None
|
||||
uk2_unit: str | None = None
|
||||
forecast_mode: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
SENSOR_TYPES: dict[str, DarkskySensorEntityDescription] = {
|
||||
"summary": DarkskySensorEntityDescription(
|
||||
key="summary",
|
||||
name="Summary",
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"minutely_summary": DarkskySensorEntityDescription(
|
||||
key="minutely_summary",
|
||||
name="Minutely Summary",
|
||||
forecast_mode=[],
|
||||
),
|
||||
"hourly_summary": DarkskySensorEntityDescription(
|
||||
key="hourly_summary",
|
||||
name="Hourly Summary",
|
||||
forecast_mode=[],
|
||||
),
|
||||
"daily_summary": DarkskySensorEntityDescription(
|
||||
key="daily_summary",
|
||||
name="Daily Summary",
|
||||
forecast_mode=[],
|
||||
),
|
||||
"icon": DarkskySensorEntityDescription(
|
||||
key="icon",
|
||||
name="Icon",
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"nearest_storm_distance": DarkskySensorEntityDescription(
|
||||
key="nearest_storm_distance",
|
||||
name="Nearest Storm Distance",
|
||||
si_unit=UnitOfLength.KILOMETERS,
|
||||
us_unit=UnitOfLength.MILES,
|
||||
ca_unit=UnitOfLength.KILOMETERS,
|
||||
uk_unit=UnitOfLength.KILOMETERS,
|
||||
uk2_unit=UnitOfLength.MILES,
|
||||
icon="mdi:weather-lightning",
|
||||
forecast_mode=["currently"],
|
||||
),
|
||||
"nearest_storm_bearing": DarkskySensorEntityDescription(
|
||||
key="nearest_storm_bearing",
|
||||
name="Nearest Storm Bearing",
|
||||
si_unit=DEGREE,
|
||||
us_unit=DEGREE,
|
||||
ca_unit=DEGREE,
|
||||
uk_unit=DEGREE,
|
||||
uk2_unit=DEGREE,
|
||||
icon="mdi:weather-lightning",
|
||||
forecast_mode=["currently"],
|
||||
),
|
||||
"precip_type": DarkskySensorEntityDescription(
|
||||
key="precip_type",
|
||||
name="Precip",
|
||||
icon="mdi:weather-pouring",
|
||||
forecast_mode=["currently", "minutely", "hourly", "daily"],
|
||||
),
|
||||
"precip_intensity": DarkskySensorEntityDescription(
|
||||
key="precip_intensity",
|
||||
name="Precip Intensity",
|
||||
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
||||
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
icon="mdi:weather-rainy",
|
||||
forecast_mode=["currently", "minutely", "hourly", "daily"],
|
||||
),
|
||||
"precip_probability": DarkskySensorEntityDescription(
|
||||
key="precip_probability",
|
||||
name="Precip Probability",
|
||||
si_unit=PERCENTAGE,
|
||||
us_unit=PERCENTAGE,
|
||||
ca_unit=PERCENTAGE,
|
||||
uk_unit=PERCENTAGE,
|
||||
uk2_unit=PERCENTAGE,
|
||||
icon="mdi:water-percent",
|
||||
forecast_mode=["currently", "minutely", "hourly", "daily"],
|
||||
),
|
||||
"precip_accumulation": DarkskySensorEntityDescription(
|
||||
key="precip_accumulation",
|
||||
name="Precip Accumulation",
|
||||
device_class=SensorDeviceClass.PRECIPITATION,
|
||||
si_unit=UnitOfPrecipitationDepth.CENTIMETERS,
|
||||
us_unit=UnitOfPrecipitationDepth.INCHES,
|
||||
ca_unit=UnitOfPrecipitationDepth.CENTIMETERS,
|
||||
uk_unit=UnitOfPrecipitationDepth.CENTIMETERS,
|
||||
uk2_unit=UnitOfPrecipitationDepth.CENTIMETERS,
|
||||
icon="mdi:weather-snowy",
|
||||
forecast_mode=["hourly", "daily"],
|
||||
),
|
||||
"temperature": DarkskySensorEntityDescription(
|
||||
key="temperature",
|
||||
name="Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["currently", "hourly"],
|
||||
),
|
||||
"apparent_temperature": DarkskySensorEntityDescription(
|
||||
key="apparent_temperature",
|
||||
name="Apparent Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["currently", "hourly"],
|
||||
),
|
||||
"dew_point": DarkskySensorEntityDescription(
|
||||
key="dew_point",
|
||||
name="Dew Point",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"wind_speed": DarkskySensorEntityDescription(
|
||||
key="wind_speed",
|
||||
name="Wind Speed",
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
si_unit=UnitOfSpeed.METERS_PER_SECOND,
|
||||
us_unit=UnitOfSpeed.MILES_PER_HOUR,
|
||||
ca_unit=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
uk_unit=UnitOfSpeed.MILES_PER_HOUR,
|
||||
uk2_unit=UnitOfSpeed.MILES_PER_HOUR,
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"wind_bearing": DarkskySensorEntityDescription(
|
||||
key="wind_bearing",
|
||||
name="Wind Bearing",
|
||||
si_unit=DEGREE,
|
||||
us_unit=DEGREE,
|
||||
ca_unit=DEGREE,
|
||||
uk_unit=DEGREE,
|
||||
uk2_unit=DEGREE,
|
||||
icon="mdi:compass",
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"wind_gust": DarkskySensorEntityDescription(
|
||||
key="wind_gust",
|
||||
name="Wind Gust",
|
||||
device_class=SensorDeviceClass.WIND_SPEED,
|
||||
si_unit=UnitOfSpeed.METERS_PER_SECOND,
|
||||
us_unit=UnitOfSpeed.MILES_PER_HOUR,
|
||||
ca_unit=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
uk_unit=UnitOfSpeed.MILES_PER_HOUR,
|
||||
uk2_unit=UnitOfSpeed.MILES_PER_HOUR,
|
||||
icon="mdi:weather-windy-variant",
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"cloud_cover": DarkskySensorEntityDescription(
|
||||
key="cloud_cover",
|
||||
name="Cloud Coverage",
|
||||
si_unit=PERCENTAGE,
|
||||
us_unit=PERCENTAGE,
|
||||
ca_unit=PERCENTAGE,
|
||||
uk_unit=PERCENTAGE,
|
||||
uk2_unit=PERCENTAGE,
|
||||
icon="mdi:weather-partly-cloudy",
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"humidity": DarkskySensorEntityDescription(
|
||||
key="humidity",
|
||||
name="Humidity",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
si_unit=PERCENTAGE,
|
||||
us_unit=PERCENTAGE,
|
||||
ca_unit=PERCENTAGE,
|
||||
uk_unit=PERCENTAGE,
|
||||
uk2_unit=PERCENTAGE,
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"pressure": DarkskySensorEntityDescription(
|
||||
key="pressure",
|
||||
name="Pressure",
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
si_unit=UnitOfPressure.MBAR,
|
||||
us_unit=UnitOfPressure.MBAR,
|
||||
ca_unit=UnitOfPressure.MBAR,
|
||||
uk_unit=UnitOfPressure.MBAR,
|
||||
uk2_unit=UnitOfPressure.MBAR,
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"visibility": DarkskySensorEntityDescription(
|
||||
key="visibility",
|
||||
name="Visibility",
|
||||
si_unit=UnitOfLength.KILOMETERS,
|
||||
us_unit=UnitOfLength.MILES,
|
||||
ca_unit=UnitOfLength.KILOMETERS,
|
||||
uk_unit=UnitOfLength.KILOMETERS,
|
||||
uk2_unit=UnitOfLength.MILES,
|
||||
icon="mdi:eye",
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"ozone": DarkskySensorEntityDescription(
|
||||
key="ozone",
|
||||
name="Ozone",
|
||||
device_class=SensorDeviceClass.OZONE,
|
||||
si_unit="DU",
|
||||
us_unit="DU",
|
||||
ca_unit="DU",
|
||||
uk_unit="DU",
|
||||
uk2_unit="DU",
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"apparent_temperature_max": DarkskySensorEntityDescription(
|
||||
key="apparent_temperature_max",
|
||||
name="Daily High Apparent Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"apparent_temperature_high": DarkskySensorEntityDescription(
|
||||
key="apparent_temperature_high",
|
||||
name="Daytime High Apparent Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"apparent_temperature_min": DarkskySensorEntityDescription(
|
||||
key="apparent_temperature_min",
|
||||
name="Daily Low Apparent Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"apparent_temperature_low": DarkskySensorEntityDescription(
|
||||
key="apparent_temperature_low",
|
||||
name="Overnight Low Apparent Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"temperature_max": DarkskySensorEntityDescription(
|
||||
key="temperature_max",
|
||||
name="Daily High Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"temperature_high": DarkskySensorEntityDescription(
|
||||
key="temperature_high",
|
||||
name="Daytime High Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"temperature_min": DarkskySensorEntityDescription(
|
||||
key="temperature_min",
|
||||
name="Daily Low Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"temperature_low": DarkskySensorEntityDescription(
|
||||
key="temperature_low",
|
||||
name="Overnight Low Temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
si_unit=UnitOfTemperature.CELSIUS,
|
||||
us_unit=UnitOfTemperature.FAHRENHEIT,
|
||||
ca_unit=UnitOfTemperature.CELSIUS,
|
||||
uk_unit=UnitOfTemperature.CELSIUS,
|
||||
uk2_unit=UnitOfTemperature.CELSIUS,
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"precip_intensity_max": DarkskySensorEntityDescription(
|
||||
key="precip_intensity_max",
|
||||
name="Daily Max Precip Intensity",
|
||||
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
||||
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
||||
icon="mdi:thermometer",
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"uv_index": DarkskySensorEntityDescription(
|
||||
key="uv_index",
|
||||
name="UV Index",
|
||||
si_unit=UV_INDEX,
|
||||
us_unit=UV_INDEX,
|
||||
ca_unit=UV_INDEX,
|
||||
uk_unit=UV_INDEX,
|
||||
uk2_unit=UV_INDEX,
|
||||
icon="mdi:weather-sunny",
|
||||
forecast_mode=["currently", "hourly", "daily"],
|
||||
),
|
||||
"moon_phase": DarkskySensorEntityDescription(
|
||||
key="moon_phase",
|
||||
name="Moon Phase",
|
||||
icon="mdi:weather-night",
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"sunrise_time": DarkskySensorEntityDescription(
|
||||
key="sunrise_time",
|
||||
name="Sunrise",
|
||||
icon="mdi:white-balance-sunny",
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"sunset_time": DarkskySensorEntityDescription(
|
||||
key="sunset_time",
|
||||
name="Sunset",
|
||||
icon="mdi:weather-night",
|
||||
forecast_mode=["daily"],
|
||||
),
|
||||
"alerts": DarkskySensorEntityDescription(
|
||||
key="alerts",
|
||||
name="Alerts",
|
||||
icon="mdi:alert-circle-outline",
|
||||
forecast_mode=[],
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class ConditionPicture(NamedTuple):
|
||||
"""Entity picture and icon for condition."""
|
||||
|
||||
entity_picture: str
|
||||
icon: str
|
||||
|
||||
|
||||
CONDITION_PICTURES: dict[str, ConditionPicture] = {
|
||||
"clear-day": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-sunny.svg",
|
||||
icon="mdi:weather-sunny",
|
||||
),
|
||||
"clear-night": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-night.svg",
|
||||
icon="mdi:weather-night",
|
||||
),
|
||||
"rain": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-pouring.svg",
|
||||
icon="mdi:weather-pouring",
|
||||
),
|
||||
"snow": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-snowy.svg",
|
||||
icon="mdi:weather-snowy",
|
||||
),
|
||||
"sleet": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-hail.svg",
|
||||
icon="mdi:weather-snowy-rainy",
|
||||
),
|
||||
"wind": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-windy.svg",
|
||||
icon="mdi:weather-windy",
|
||||
),
|
||||
"fog": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-fog.svg",
|
||||
icon="mdi:weather-fog",
|
||||
),
|
||||
"cloudy": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-cloudy.svg",
|
||||
icon="mdi:weather-cloudy",
|
||||
),
|
||||
"partly-cloudy-day": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-partlycloudy.svg",
|
||||
icon="mdi:weather-partly-cloudy",
|
||||
),
|
||||
"partly-cloudy-night": ConditionPicture(
|
||||
entity_picture="/static/images/darksky/weather-cloudy.svg",
|
||||
icon="mdi:weather-night-partly-cloudy",
|
||||
),
|
||||
}
|
||||
|
||||
# Language Supported Codes
|
||||
LANGUAGE_CODES = [
|
||||
"ar",
|
||||
"az",
|
||||
"be",
|
||||
"bg",
|
||||
"bn",
|
||||
"bs",
|
||||
"ca",
|
||||
"cs",
|
||||
"da",
|
||||
"de",
|
||||
"el",
|
||||
"en",
|
||||
"ja",
|
||||
"ka",
|
||||
"kn",
|
||||
"ko",
|
||||
"eo",
|
||||
"es",
|
||||
"et",
|
||||
"fi",
|
||||
"fr",
|
||||
"he",
|
||||
"hi",
|
||||
"hr",
|
||||
"hu",
|
||||
"id",
|
||||
"is",
|
||||
"it",
|
||||
"kw",
|
||||
"lv",
|
||||
"ml",
|
||||
"mr",
|
||||
"nb",
|
||||
"nl",
|
||||
"pa",
|
||||
"pl",
|
||||
"pt",
|
||||
"ro",
|
||||
"ru",
|
||||
"sk",
|
||||
"sl",
|
||||
"sr",
|
||||
"sv",
|
||||
"ta",
|
||||
"te",
|
||||
"tet",
|
||||
"tr",
|
||||
"uk",
|
||||
"ur",
|
||||
"x-pig-latin",
|
||||
"zh",
|
||||
"zh-tw",
|
||||
]
|
||||
|
||||
ALLOWED_UNITS = ["auto", "si", "us", "ca", "uk", "uk2"]
|
||||
|
||||
ALERTS_ATTRS = ["time", "description", "expires", "severity", "uri", "regions", "title"]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MONITORED_CONDITIONS): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
||||
),
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_UNITS): vol.In(ALLOWED_UNITS),
|
||||
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(LANGUAGE_CODES),
|
||||
vol.Inclusive(
|
||||
CONF_LATITUDE, "coordinates", "Latitude and longitude must exist together"
|
||||
): cv.latitude,
|
||||
vol.Inclusive(
|
||||
CONF_LONGITUDE, "coordinates", "Latitude and longitude must exist together"
|
||||
): cv.longitude,
|
||||
vol.Optional(CONF_FORECAST): vol.All(cv.ensure_list, [vol.Range(min=0, max=7)]),
|
||||
vol.Optional(CONF_HOURLY_FORECAST): vol.All(
|
||||
cv.ensure_list, [vol.Range(min=0, max=48)]
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Dark Sky sensor."""
|
||||
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
||||
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
||||
language = config.get(CONF_LANGUAGE)
|
||||
interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
|
||||
|
||||
if CONF_UNITS in config:
|
||||
units = config[CONF_UNITS]
|
||||
elif hass.config.units is METRIC_SYSTEM:
|
||||
units = "si"
|
||||
else:
|
||||
units = "us"
|
||||
|
||||
forecast_data = DarkSkyData(
|
||||
api_key=config.get(CONF_API_KEY),
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
units=units,
|
||||
language=language,
|
||||
interval=interval,
|
||||
)
|
||||
forecast_data.update()
|
||||
forecast_data.update_currently()
|
||||
|
||||
# If connection failed don't setup platform.
|
||||
if forecast_data.data is None:
|
||||
return
|
||||
|
||||
name = config.get(CONF_NAME)
|
||||
|
||||
forecast = config.get(CONF_FORECAST)
|
||||
forecast_hour = config.get(CONF_HOURLY_FORECAST)
|
||||
sensors: list[SensorEntity] = []
|
||||
for variable in config[CONF_MONITORED_CONDITIONS]:
|
||||
if variable in DEPRECATED_SENSOR_TYPES:
|
||||
_LOGGER.warning("Monitored condition %s is deprecated", variable)
|
||||
description = SENSOR_TYPES[variable]
|
||||
if not description.forecast_mode or "currently" in description.forecast_mode:
|
||||
if variable == "alerts":
|
||||
sensors.append(DarkSkyAlertSensor(forecast_data, description, name))
|
||||
else:
|
||||
sensors.append(DarkSkySensor(forecast_data, description, name))
|
||||
|
||||
if forecast is not None and "daily" in description.forecast_mode:
|
||||
sensors.extend(
|
||||
[
|
||||
DarkSkySensor(
|
||||
forecast_data, description, name, forecast_day=forecast_day
|
||||
)
|
||||
for forecast_day in forecast
|
||||
]
|
||||
)
|
||||
if forecast_hour is not None and "hourly" in description.forecast_mode:
|
||||
sensors.extend(
|
||||
[
|
||||
DarkSkySensor(
|
||||
forecast_data, description, name, forecast_hour=forecast_h
|
||||
)
|
||||
for forecast_h in forecast_hour
|
||||
]
|
||||
)
|
||||
|
||||
add_entities(sensors, True)
|
||||
|
||||
|
||||
class DarkSkySensor(SensorEntity):
|
||||
"""Implementation of a Dark Sky sensor."""
|
||||
|
||||
_attr_attribution = "Powered by Dark Sky"
|
||||
entity_description: DarkskySensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
forecast_data,
|
||||
description: DarkskySensorEntityDescription,
|
||||
name,
|
||||
forecast_day=None,
|
||||
forecast_hour=None,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self.entity_description = description
|
||||
self.forecast_data = forecast_data
|
||||
self.forecast_day = forecast_day
|
||||
self.forecast_hour = forecast_hour
|
||||
self._icon: str | None = None
|
||||
|
||||
if forecast_day is not None:
|
||||
self._attr_name = f"{name} {description.name} {forecast_day}d"
|
||||
elif forecast_hour is not None:
|
||||
self._attr_name = f"{name} {description.name} {forecast_hour}h"
|
||||
else:
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
|
||||
@property
|
||||
def unit_system(self):
|
||||
"""Return the unit system of this entity."""
|
||||
return self.forecast_data.unit_system
|
||||
|
||||
@property
|
||||
def entity_picture(self) -> str | None:
|
||||
"""Return the entity picture to use in the frontend, if any."""
|
||||
if self._icon is None or "summary" not in self.entity_description.key:
|
||||
return None
|
||||
|
||||
if self._icon in CONDITION_PICTURES:
|
||||
return CONDITION_PICTURES[self._icon].entity_picture
|
||||
|
||||
return None
|
||||
|
||||
def update_unit_of_measurement(self) -> None:
|
||||
"""Update units based on unit system."""
|
||||
unit_key = MAP_UNIT_SYSTEM.get(self.unit_system, "si_unit")
|
||||
self._attr_native_unit_of_measurement = getattr(
|
||||
self.entity_description, unit_key
|
||||
)
|
||||
|
||||
@property
|
||||
def icon(self) -> str | None:
|
||||
"""Icon to use in the frontend, if any."""
|
||||
if (
|
||||
"summary" in self.entity_description.key
|
||||
and self._icon in CONDITION_PICTURES
|
||||
):
|
||||
return CONDITION_PICTURES[self._icon].icon
|
||||
|
||||
return self.entity_description.icon
|
||||
|
||||
def update(self) -> None:
|
||||
"""Get the latest data from Dark Sky and updates the states."""
|
||||
# Call the API for new forecast data. Each sensor will re-trigger this
|
||||
# same exact call, but that's fine. We cache results for a short period
|
||||
# of time to prevent hitting API limits. Note that Dark Sky will
|
||||
# charge users for too many calls in 1 day, so take care when updating.
|
||||
self.forecast_data.update()
|
||||
self.update_unit_of_measurement()
|
||||
|
||||
sensor_type = self.entity_description.key
|
||||
if sensor_type == "minutely_summary":
|
||||
self.forecast_data.update_minutely()
|
||||
minutely = self.forecast_data.data_minutely
|
||||
self._attr_native_value = getattr(minutely, "summary", "")
|
||||
self._icon = getattr(minutely, "icon", "")
|
||||
elif sensor_type == "hourly_summary":
|
||||
self.forecast_data.update_hourly()
|
||||
hourly = self.forecast_data.data_hourly
|
||||
self._attr_native_value = getattr(hourly, "summary", "")
|
||||
self._icon = getattr(hourly, "icon", "")
|
||||
elif self.forecast_hour is not None:
|
||||
self.forecast_data.update_hourly()
|
||||
hourly = self.forecast_data.data_hourly
|
||||
if hasattr(hourly, "data"):
|
||||
self._attr_native_value = self.get_state(
|
||||
hourly.data[self.forecast_hour]
|
||||
)
|
||||
else:
|
||||
self._attr_native_value = 0
|
||||
elif sensor_type == "daily_summary":
|
||||
self.forecast_data.update_daily()
|
||||
daily = self.forecast_data.data_daily
|
||||
self._attr_native_value = getattr(daily, "summary", "")
|
||||
self._icon = getattr(daily, "icon", "")
|
||||
elif self.forecast_day is not None:
|
||||
self.forecast_data.update_daily()
|
||||
daily = self.forecast_data.data_daily
|
||||
if hasattr(daily, "data"):
|
||||
self._attr_native_value = self.get_state(daily.data[self.forecast_day])
|
||||
else:
|
||||
self._attr_native_value = 0
|
||||
else:
|
||||
self.forecast_data.update_currently()
|
||||
currently = self.forecast_data.data_currently
|
||||
self._attr_native_value = self.get_state(currently)
|
||||
|
||||
def get_state(self, data):
|
||||
"""Return a new state based on the type.
|
||||
|
||||
If the sensor type is unknown, the current state is returned.
|
||||
"""
|
||||
sensor_type = self.entity_description.key
|
||||
lookup_type = convert_to_camel(sensor_type)
|
||||
|
||||
if (state := getattr(data, lookup_type, None)) is None:
|
||||
return None
|
||||
|
||||
if "summary" in sensor_type:
|
||||
self._icon = getattr(data, "icon", "")
|
||||
|
||||
# Some state data needs to be rounded to whole values or converted to
|
||||
# percentages
|
||||
if sensor_type in {"precip_probability", "cloud_cover", "humidity"}:
|
||||
return round(state * 100, 1)
|
||||
|
||||
if sensor_type in {
|
||||
"dew_point",
|
||||
"temperature",
|
||||
"apparent_temperature",
|
||||
"temperature_low",
|
||||
"apparent_temperature_low",
|
||||
"temperature_min",
|
||||
"apparent_temperature_min",
|
||||
"temperature_high",
|
||||
"apparent_temperature_high",
|
||||
"temperature_max",
|
||||
"apparent_temperature_max",
|
||||
"precip_accumulation",
|
||||
"pressure",
|
||||
"ozone",
|
||||
"uvIndex",
|
||||
}:
|
||||
return round(state, 1)
|
||||
return state
|
||||
|
||||
|
||||
class DarkSkyAlertSensor(SensorEntity):
|
||||
"""Implementation of a Dark Sky sensor."""
|
||||
|
||||
entity_description: DarkskySensorEntityDescription
|
||||
_attr_native_value: int | None
|
||||
|
||||
def __init__(
|
||||
self, forecast_data, description: DarkskySensorEntityDescription, name
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self.entity_description = description
|
||||
self.forecast_data = forecast_data
|
||||
self._alerts = None
|
||||
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Icon to use in the frontend, if any."""
|
||||
if self._attr_native_value is not None and self._attr_native_value > 0:
|
||||
return "mdi:alert-circle"
|
||||
return "mdi:alert-circle-outline"
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return self._alerts
|
||||
|
||||
def update(self) -> None:
|
||||
"""Get the latest data from Dark Sky and updates the states."""
|
||||
# Call the API for new forecast data. Each sensor will re-trigger this
|
||||
# same exact call, but that's fine. We cache results for a short period
|
||||
# of time to prevent hitting API limits. Note that Dark Sky will
|
||||
# charge users for too many calls in 1 day, so take care when updating.
|
||||
self.forecast_data.update()
|
||||
self.forecast_data.update_alerts()
|
||||
alerts = self.forecast_data.data_alerts
|
||||
self._attr_native_value = self.get_state(alerts)
|
||||
|
||||
def get_state(self, data):
|
||||
"""Return a new state based on the type.
|
||||
|
||||
If the sensor type is unknown, the current state is returned.
|
||||
"""
|
||||
alerts = {}
|
||||
if data is None:
|
||||
self._alerts = alerts
|
||||
return data
|
||||
|
||||
multiple_alerts = len(data) > 1
|
||||
for i, alert in enumerate(data):
|
||||
for attr in ALERTS_ATTRS:
|
||||
if multiple_alerts:
|
||||
dkey = f"{attr}_{i!s}"
|
||||
else:
|
||||
dkey = attr
|
||||
alerts[dkey] = getattr(alert, attr)
|
||||
self._alerts = alerts
|
||||
|
||||
return len(data)
|
||||
|
||||
|
||||
def convert_to_camel(data):
|
||||
"""Convert snake case (foo_bar_bat) to camel case (fooBarBat).
|
||||
|
||||
This is not pythonic, but needed for certain situations.
|
||||
"""
|
||||
components = data.split("_")
|
||||
capital_components = "".join(x.title() for x in components[1:])
|
||||
return f"{components[0]}{capital_components}"
|
||||
|
||||
|
||||
class DarkSkyData:
|
||||
"""Get the latest data from Darksky."""
|
||||
|
||||
def __init__(self, api_key, latitude, longitude, units, language, interval):
|
||||
"""Initialize the data object."""
|
||||
self._api_key = api_key
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.units = units
|
||||
self.language = language
|
||||
self._connect_error = False
|
||||
|
||||
self.data = None
|
||||
self.unit_system = None
|
||||
self.data_currently = None
|
||||
self.data_minutely = None
|
||||
self.data_hourly = None
|
||||
self.data_daily = None
|
||||
self.data_alerts = None
|
||||
|
||||
# Apply throttling to methods using configured interval
|
||||
self.update = Throttle(interval)(self._update)
|
||||
self.update_currently = Throttle(interval)(self._update_currently)
|
||||
self.update_minutely = Throttle(interval)(self._update_minutely)
|
||||
self.update_hourly = Throttle(interval)(self._update_hourly)
|
||||
self.update_daily = Throttle(interval)(self._update_daily)
|
||||
self.update_alerts = Throttle(interval)(self._update_alerts)
|
||||
|
||||
def _update(self):
|
||||
"""Get the latest data from Dark Sky."""
|
||||
try:
|
||||
self.data = forecastio.load_forecast(
|
||||
self._api_key,
|
||||
self.latitude,
|
||||
self.longitude,
|
||||
units=self.units,
|
||||
lang=self.language,
|
||||
)
|
||||
if self._connect_error:
|
||||
self._connect_error = False
|
||||
_LOGGER.info("Reconnected to Dark Sky")
|
||||
except (ConnectError, HTTPError, Timeout, ValueError) as error:
|
||||
if not self._connect_error:
|
||||
self._connect_error = True
|
||||
_LOGGER.error("Unable to connect to Dark Sky: %s", error)
|
||||
self.data = None
|
||||
self.unit_system = self.data and self.data.json["flags"]["units"]
|
||||
|
||||
def _update_currently(self):
|
||||
"""Update currently data."""
|
||||
self.data_currently = self.data and self.data.currently()
|
||||
|
||||
def _update_minutely(self):
|
||||
"""Update minutely data."""
|
||||
self.data_minutely = self.data and self.data.minutely()
|
||||
|
||||
def _update_hourly(self):
|
||||
"""Update hourly data."""
|
||||
self.data_hourly = self.data and self.data.hourly()
|
||||
|
||||
def _update_daily(self):
|
||||
"""Update daily data."""
|
||||
self.data_daily = self.data and self.data.daily()
|
||||
|
||||
def _update_alerts(self):
|
||||
"""Update alerts data."""
|
||||
self.data_alerts = self.data and self.data.alerts()
|
||||
@@ -1,281 +0,0 @@
|
||||
"""Support for retrieving meteorological data from Dark Sky."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import forecastio
|
||||
from requests.exceptions import ConnectionError as ConnectError, HTTPError, Timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
ATTR_CONDITION_FOG,
|
||||
ATTR_CONDITION_HAIL,
|
||||
ATTR_CONDITION_LIGHTNING,
|
||||
ATTR_CONDITION_PARTLYCLOUDY,
|
||||
ATTR_CONDITION_RAINY,
|
||||
ATTR_CONDITION_SNOWY,
|
||||
ATTR_CONDITION_SNOWY_RAINY,
|
||||
ATTR_CONDITION_SUNNY,
|
||||
ATTR_CONDITION_WINDY,
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
PLATFORM_SCHEMA,
|
||||
WeatherEntity,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_MODE,
|
||||
CONF_NAME,
|
||||
UnitOfLength,
|
||||
UnitOfPrecipitationDepth,
|
||||
UnitOfPressure,
|
||||
UnitOfSpeed,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTRIBUTION = "Powered by Dark Sky"
|
||||
|
||||
FORECAST_MODE = ["hourly", "daily"]
|
||||
|
||||
MAP_CONDITION = {
|
||||
"clear-day": ATTR_CONDITION_SUNNY,
|
||||
"clear-night": ATTR_CONDITION_CLEAR_NIGHT,
|
||||
"rain": ATTR_CONDITION_RAINY,
|
||||
"snow": ATTR_CONDITION_SNOWY,
|
||||
"sleet": ATTR_CONDITION_SNOWY_RAINY,
|
||||
"wind": ATTR_CONDITION_WINDY,
|
||||
"fog": ATTR_CONDITION_FOG,
|
||||
"cloudy": ATTR_CONDITION_CLOUDY,
|
||||
"partly-cloudy-day": ATTR_CONDITION_PARTLYCLOUDY,
|
||||
"partly-cloudy-night": ATTR_CONDITION_PARTLYCLOUDY,
|
||||
"hail": ATTR_CONDITION_HAIL,
|
||||
"thunderstorm": ATTR_CONDITION_LIGHTNING,
|
||||
"tornado": None,
|
||||
}
|
||||
|
||||
CONF_UNITS = "units"
|
||||
|
||||
DEFAULT_NAME = "Dark Sky"
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(
|
||||
cv.removed(CONF_UNITS),
|
||||
PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Optional(CONF_LATITUDE): cv.latitude,
|
||||
vol.Optional(CONF_LONGITUDE): cv.longitude,
|
||||
vol.Optional(CONF_MODE, default="hourly"): vol.In(FORECAST_MODE),
|
||||
vol.Optional(CONF_UNITS): vol.In(["auto", "si", "us", "ca", "uk", "uk2"]),
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=3)
|
||||
|
||||
|
||||
def setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Dark Sky weather."""
|
||||
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
||||
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
||||
name = config.get(CONF_NAME)
|
||||
mode = config.get(CONF_MODE)
|
||||
|
||||
units = "si"
|
||||
dark_sky = DarkSkyData(config.get(CONF_API_KEY), latitude, longitude, units)
|
||||
|
||||
add_entities([DarkSkyWeather(name, dark_sky, mode)], True)
|
||||
|
||||
|
||||
class DarkSkyWeather(WeatherEntity):
|
||||
"""Representation of a weather condition."""
|
||||
|
||||
_attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS
|
||||
_attr_native_pressure_unit = UnitOfPressure.MBAR
|
||||
_attr_native_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_native_visibility_unit = UnitOfLength.KILOMETERS
|
||||
_attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND
|
||||
|
||||
def __init__(self, name, dark_sky, mode):
|
||||
"""Initialize Dark Sky weather."""
|
||||
self._name = name
|
||||
self._dark_sky = dark_sky
|
||||
self._mode = mode
|
||||
|
||||
self._ds_data = None
|
||||
self._ds_currently = None
|
||||
self._ds_hourly = None
|
||||
self._ds_daily = None
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if weather data is available from Dark Sky."""
|
||||
return self._ds_data is not None
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
"""Return the attribution."""
|
||||
return ATTRIBUTION
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def native_temperature(self):
|
||||
"""Return the temperature."""
|
||||
return self._ds_currently.get("temperature")
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
"""Return the humidity."""
|
||||
return round(self._ds_currently.get("humidity") * 100.0, 2)
|
||||
|
||||
@property
|
||||
def native_wind_speed(self):
|
||||
"""Return the wind speed."""
|
||||
return self._ds_currently.get("windSpeed")
|
||||
|
||||
@property
|
||||
def wind_bearing(self):
|
||||
"""Return the wind bearing."""
|
||||
return self._ds_currently.get("windBearing")
|
||||
|
||||
@property
|
||||
def ozone(self):
|
||||
"""Return the ozone level."""
|
||||
return self._ds_currently.get("ozone")
|
||||
|
||||
@property
|
||||
def native_pressure(self):
|
||||
"""Return the pressure."""
|
||||
return self._ds_currently.get("pressure")
|
||||
|
||||
@property
|
||||
def native_visibility(self):
|
||||
"""Return the visibility."""
|
||||
return self._ds_currently.get("visibility")
|
||||
|
||||
@property
|
||||
def condition(self):
|
||||
"""Return the weather condition."""
|
||||
return MAP_CONDITION.get(self._ds_currently.get("icon"))
|
||||
|
||||
@property
|
||||
def forecast(self):
|
||||
"""Return the forecast array."""
|
||||
|
||||
# Per conversation with Joshua Reyes of Dark Sky, to get the total
|
||||
# forecasted precipitation, you have to multiple the intensity by
|
||||
# the hours for the forecast interval
|
||||
def calc_precipitation(intensity, hours):
|
||||
amount = None
|
||||
if intensity is not None:
|
||||
amount = round((intensity * hours), 1)
|
||||
return amount if amount > 0 else None
|
||||
|
||||
data = None
|
||||
|
||||
if self._mode == "daily":
|
||||
data = [
|
||||
{
|
||||
ATTR_FORECAST_TIME: utc_from_timestamp(
|
||||
entry.d.get("time")
|
||||
).isoformat(),
|
||||
ATTR_FORECAST_NATIVE_TEMP: entry.d.get("temperatureHigh"),
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW: entry.d.get("temperatureLow"),
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION: calc_precipitation(
|
||||
entry.d.get("precipIntensity"), 24
|
||||
),
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED: entry.d.get("windSpeed"),
|
||||
ATTR_FORECAST_WIND_BEARING: entry.d.get("windBearing"),
|
||||
ATTR_FORECAST_CONDITION: MAP_CONDITION.get(entry.d.get("icon")),
|
||||
}
|
||||
for entry in self._ds_daily.data
|
||||
]
|
||||
else:
|
||||
data = [
|
||||
{
|
||||
ATTR_FORECAST_TIME: utc_from_timestamp(
|
||||
entry.d.get("time")
|
||||
).isoformat(),
|
||||
ATTR_FORECAST_NATIVE_TEMP: entry.d.get("temperature"),
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION: calc_precipitation(
|
||||
entry.d.get("precipIntensity"), 1
|
||||
),
|
||||
ATTR_FORECAST_CONDITION: MAP_CONDITION.get(entry.d.get("icon")),
|
||||
}
|
||||
for entry in self._ds_hourly.data
|
||||
]
|
||||
|
||||
return data
|
||||
|
||||
def update(self) -> None:
|
||||
"""Get the latest data from Dark Sky."""
|
||||
self._dark_sky.update()
|
||||
|
||||
self._ds_data = self._dark_sky.data
|
||||
currently = self._dark_sky.currently
|
||||
self._ds_currently = currently.d if currently else {}
|
||||
self._ds_hourly = self._dark_sky.hourly
|
||||
self._ds_daily = self._dark_sky.daily
|
||||
|
||||
|
||||
class DarkSkyData:
|
||||
"""Get the latest data from Dark Sky."""
|
||||
|
||||
def __init__(self, api_key, latitude, longitude, units):
|
||||
"""Initialize the data object."""
|
||||
self._api_key = api_key
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.requested_units = units
|
||||
|
||||
self.data = None
|
||||
self.currently = None
|
||||
self.hourly = None
|
||||
self.daily = None
|
||||
self._connect_error = False
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
"""Get the latest data from Dark Sky."""
|
||||
try:
|
||||
self.data = forecastio.load_forecast(
|
||||
self._api_key, self.latitude, self.longitude, units=self.requested_units
|
||||
)
|
||||
self.currently = self.data.currently()
|
||||
self.hourly = self.data.hourly()
|
||||
self.daily = self.data.daily()
|
||||
if self._connect_error:
|
||||
self._connect_error = False
|
||||
_LOGGER.info("Reconnected to Dark Sky")
|
||||
except (ConnectError, HTTPError, Timeout, ValueError) as error:
|
||||
if not self._connect_error:
|
||||
self._connect_error = True
|
||||
_LOGGER.error("Unable to connect to Dark Sky. %s", error)
|
||||
self.data = None
|
||||
@@ -427,7 +427,7 @@ def async_setup_scanner_platform(
|
||||
hass,
|
||||
async_device_tracker_scan,
|
||||
interval,
|
||||
f"device_tracker {platform} legacy scan",
|
||||
name=f"device_tracker {platform} legacy scan",
|
||||
)
|
||||
hass.async_create_task(async_device_tracker_scan(None))
|
||||
|
||||
|
||||
@@ -260,7 +260,10 @@ class NetworkWatcher(WatcherBase):
|
||||
"""Start scanning for new devices on the network."""
|
||||
self._discover_hosts = DiscoverHosts()
|
||||
self._unsub = async_track_time_interval(
|
||||
self.hass, self.async_start_discover, SCAN_INTERVAL, "DHCP network watcher"
|
||||
self.hass,
|
||||
self.async_start_discover,
|
||||
SCAN_INTERVAL,
|
||||
name="DHCP network watcher",
|
||||
)
|
||||
self.async_start_discover()
|
||||
|
||||
|
||||
@@ -48,49 +48,49 @@ class DSMRReaderSensorEntityDescription(SensorEntityDescription):
|
||||
SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/electricity_delivered_1",
|
||||
name="Low tariff usage",
|
||||
translation_key="low_tariff_usage",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/electricity_returned_1",
|
||||
name="Low tariff returned",
|
||||
translation_key="low_tariff_returned",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/electricity_delivered_2",
|
||||
name="High tariff usage",
|
||||
translation_key="high_tariff_usage",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/electricity_returned_2",
|
||||
name="High tariff returned",
|
||||
translation_key="high_tariff_returned",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/electricity_currently_delivered",
|
||||
name="Current power usage",
|
||||
translation_key="current_power_usage",
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/electricity_currently_returned",
|
||||
name="Current power return",
|
||||
translation_key="current_power_return",
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_currently_delivered_l1",
|
||||
name="Current power usage L1",
|
||||
translation_key="current_power_usage_l1",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
@@ -98,7 +98,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_currently_delivered_l2",
|
||||
name="Current power usage L2",
|
||||
translation_key="current_power_usage_l2",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
@@ -106,7 +106,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_currently_delivered_l3",
|
||||
name="Current power usage L3",
|
||||
translation_key="current_power_usage_l3",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
@@ -114,7 +114,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_currently_returned_l1",
|
||||
name="Current power return L1",
|
||||
translation_key="current_power_return_l1",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
@@ -122,7 +122,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_currently_returned_l2",
|
||||
name="Current power return L2",
|
||||
translation_key="current_power_return_l2",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
@@ -130,7 +130,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_currently_returned_l3",
|
||||
name="Current power return L3",
|
||||
translation_key="current_power_return_l3",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
@@ -138,7 +138,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/extra_device_delivered",
|
||||
name="Gas meter usage",
|
||||
translation_key="gas_meter_usage",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:fire",
|
||||
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||
@@ -146,7 +146,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_voltage_l1",
|
||||
name="Current voltage L1",
|
||||
translation_key="current_voltage_l1",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
@@ -154,7 +154,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_voltage_l2",
|
||||
name="Current voltage L2",
|
||||
translation_key="current_voltage_l2",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
@@ -162,7 +162,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_voltage_l3",
|
||||
name="Current voltage L3",
|
||||
translation_key="current_voltage_l3",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
@@ -170,7 +170,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_power_current_l1",
|
||||
name="Phase power current L1",
|
||||
translation_key="phase_power_current_l1",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
@@ -178,7 +178,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_power_current_l2",
|
||||
name="Phase power current L2",
|
||||
translation_key="phase_power_current_l2",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
@@ -186,7 +186,7 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/phase_power_current_l3",
|
||||
name="Phase power current L3",
|
||||
translation_key="phase_power_current_l3",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
@@ -194,384 +194,386 @@ SENSORS: tuple[DSMRReaderSensorEntityDescription, ...] = (
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/reading/timestamp",
|
||||
name="Telegram timestamp",
|
||||
translation_key="telegram_timestamp",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state=dt_util.parse_datetime,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/consumption/gas/delivered",
|
||||
name="Gas usage",
|
||||
translation_key="gas_usage",
|
||||
device_class=SensorDeviceClass.GAS,
|
||||
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/consumption/gas/currently_delivered",
|
||||
name="Current gas usage",
|
||||
translation_key="current_gas_usage",
|
||||
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/consumption/gas/read_at",
|
||||
name="Gas meter read",
|
||||
translation_key="gas_meter_read",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state=dt_util.parse_datetime,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity1",
|
||||
name="Low tariff usage (daily)",
|
||||
translation_key="daily_low_tariff_usage",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity2",
|
||||
name="High tariff usage (daily)",
|
||||
translation_key="daily_high_tariff_usage",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity1_returned",
|
||||
name="Low tariff return (daily)",
|
||||
translation_key="daily_low_tariff_return",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity2_returned",
|
||||
name="High tariff return (daily)",
|
||||
translation_key="daily_high_tariff_return",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity_merged",
|
||||
name="Power usage total (daily)",
|
||||
translation_key="daily_power_usage_total",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity_returned_merged",
|
||||
name="Power return total (daily)",
|
||||
translation_key="daily_power_return_total",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity1_cost",
|
||||
name="Low tariff cost (daily)",
|
||||
translation_key="daily_low_tariff_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity2_cost",
|
||||
name="High tariff cost (daily)",
|
||||
translation_key="daily_high_tariff_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/electricity_cost_merged",
|
||||
name="Power total cost (daily)",
|
||||
translation_key="daily_power_total_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/gas",
|
||||
name="Gas usage (daily)",
|
||||
translation_key="daily_gas_usage",
|
||||
icon="mdi:counter",
|
||||
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/gas_cost",
|
||||
name="Gas cost",
|
||||
translation_key="gas_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/total_cost",
|
||||
name="Total cost",
|
||||
translation_key="total_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/energy_supplier_price_electricity_delivered_1",
|
||||
name="Low tariff delivered price",
|
||||
translation_key="low_tariff_delivered_price",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=PRICE_EUR_KWH,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/energy_supplier_price_electricity_delivered_2",
|
||||
name="High tariff delivered price",
|
||||
translation_key="high_tariff_delivered_price",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=PRICE_EUR_KWH,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/energy_supplier_price_electricity_returned_1",
|
||||
name="Low tariff returned price",
|
||||
translation_key="low_tariff_returned_price",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=PRICE_EUR_KWH,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/energy_supplier_price_electricity_returned_2",
|
||||
name="High tariff returned price",
|
||||
translation_key="high_tariff_returned_price",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=PRICE_EUR_KWH,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/energy_supplier_price_gas",
|
||||
name="Gas price",
|
||||
translation_key="gas_price",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=PRICE_EUR_M3,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/day-consumption/fixed_cost",
|
||||
name="Current day fixed cost",
|
||||
translation_key="current_day_fixed_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/dsmr_version",
|
||||
name="DSMR version",
|
||||
translation_key="dsmr_version",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:alert-circle",
|
||||
state=dsmr_transform,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/electricity_tariff",
|
||||
name="Electricity tariff",
|
||||
translation_key="electricity_tariff",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["low", "high"],
|
||||
icon="mdi:flash",
|
||||
state=tariff_transform,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/power_failure_count",
|
||||
name="Power failure count",
|
||||
translation_key="power_failure_count",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/long_power_failure_count",
|
||||
name="Long power failure count",
|
||||
translation_key="long_power_failure_count",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/voltage_sag_count_l1",
|
||||
name="Voltage sag L1",
|
||||
translation_key="voltage_sag_l1",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/voltage_sag_count_l2",
|
||||
name="Voltage sag L2",
|
||||
translation_key="voltage_sag_l2",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/voltage_sag_count_l3",
|
||||
name="Voltage sag L3",
|
||||
translation_key="voltage_sag_l3",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/voltage_swell_count_l1",
|
||||
name="Voltage swell L1",
|
||||
translation_key="voltage_swell_l1",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/voltage_swell_count_l2",
|
||||
name="Voltage swell L2",
|
||||
translation_key="voltage_swell_l2",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/voltage_swell_count_l3",
|
||||
name="Voltage swell L3",
|
||||
translation_key="voltage_swell_l3",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/meter-stats/rejected_telegrams",
|
||||
name="Rejected telegrams",
|
||||
translation_key="rejected_telegrams",
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:flash",
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity1",
|
||||
name="Current month low tariff usage",
|
||||
translation_key="current_month_low_tariff_usage",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity2",
|
||||
name="Current month high tariff usage",
|
||||
translation_key="current_month_high_tariff_usage",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity1_returned",
|
||||
name="Current month low tariff returned",
|
||||
translation_key="current_month_low_tariff_returned",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity2_returned",
|
||||
name="Current month high tariff returned",
|
||||
translation_key="current_month_high_tariff_returned",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity_merged",
|
||||
name="Current month power usage total",
|
||||
translation_key="current_month_power_usage_total",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity_returned_merged",
|
||||
name="Current month power return total",
|
||||
translation_key="current_month_power_return_total",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity1_cost",
|
||||
name="Current month low tariff cost",
|
||||
translation_key="current_month_low_tariff_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity2_cost",
|
||||
name="Current month high tariff cost",
|
||||
translation_key="current_month_high_tariff_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/electricity_cost_merged",
|
||||
name="Current month power total cost",
|
||||
translation_key="current_month_power_total_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/gas",
|
||||
name="Current month gas usage",
|
||||
translation_key="current_month_gas_usage",
|
||||
icon="mdi:counter",
|
||||
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/gas_cost",
|
||||
name="Current month gas cost",
|
||||
translation_key="current_month_gas_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/fixed_cost",
|
||||
name="Current month fixed cost",
|
||||
translation_key="current_month_fixed_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-month/total_cost",
|
||||
name="Current month total cost",
|
||||
translation_key="current_month_total_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity1",
|
||||
name="Current year low tariff usage",
|
||||
translation_key="current_year_low_tariff_usage",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity2",
|
||||
name="Current year high tariff usage",
|
||||
translation_key="current_year_high_tariff_usage",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity1_returned",
|
||||
name="Current year low tariff returned",
|
||||
translation_key="current_year_low_tariff_returned",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity2_returned",
|
||||
name="Current year high tariff returned",
|
||||
translation_key="current_year_high_tariff_returned",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity_merged",
|
||||
name="Current year power usage total",
|
||||
translation_key="current_year_power_usage_total",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity_returned_merged",
|
||||
name="Current year power returned total",
|
||||
translation_key="current_year_power_returned_total",
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity1_cost",
|
||||
name="Current year low tariff cost",
|
||||
translation_key="current_year_low_tariff_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity2_cost",
|
||||
name="Current year high tariff cost",
|
||||
translation_key="current_year_high_tariff_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/electricity_cost_merged",
|
||||
name="Current year power total cost",
|
||||
translation_key="current_year_power_total_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/gas",
|
||||
name="Current year gas usage",
|
||||
translation_key="current_year_gas_usage",
|
||||
icon="mdi:counter",
|
||||
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/gas_cost",
|
||||
name="Current year gas cost",
|
||||
translation_key="current_year_gas_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/fixed_cost",
|
||||
name="Current year fixed cost",
|
||||
translation_key="current_year_fixed_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/current-year/total_cost",
|
||||
name="Current year total cost",
|
||||
translation_key="current_year_total_cost",
|
||||
icon="mdi:currency-eur",
|
||||
native_unit_of_measurement=CURRENCY_EURO,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/consumption/quarter-hour-peak-electricity/average_delivered",
|
||||
name="Previous quarter-hour peak usage",
|
||||
translation_key="previous_quarter_hour_peak_usage",
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/consumption/quarter-hour-peak-electricity/read_at_start",
|
||||
name="Quarter-hour peak start time",
|
||||
translation_key="quarter_hour_peak_start_time",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state=dt_util.parse_datetime,
|
||||
),
|
||||
DSMRReaderSensorEntityDescription(
|
||||
key="dsmr/consumption/quarter-hour-peak-electricity/read_at_end",
|
||||
name="Quarter-hour peak end time",
|
||||
translation_key="quarter_hour_peak_end_time",
|
||||
entity_registry_enabled_default=False,
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state=dt_util.parse_datetime,
|
||||
|
||||
@@ -23,6 +23,7 @@ async def async_setup_entry(
|
||||
class DSMRSensor(SensorEntity):
|
||||
"""Representation of a DSMR sensor that is updated via MQTT."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: DSMRReaderSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -8,5 +8,256 @@
|
||||
"description": "Make sure to configure the 'split topic' data sources in DSMR Reader."
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"low_tariff_usage": {
|
||||
"name": "Low tariff usage"
|
||||
},
|
||||
"low_tariff_returned": {
|
||||
"name": "Low tariff returned"
|
||||
},
|
||||
"high_tariff_usage": {
|
||||
"name": "High tariff usage"
|
||||
},
|
||||
"high_tariff_returned": {
|
||||
"name": "High tariff returned"
|
||||
},
|
||||
"current_power_usage": {
|
||||
"name": "Current power usage"
|
||||
},
|
||||
"current_power_return": {
|
||||
"name": "Current power return"
|
||||
},
|
||||
"current_power_usage_l1": {
|
||||
"name": "Current power usage L1"
|
||||
},
|
||||
"current_power_usage_l2": {
|
||||
"name": "Current power usage L2"
|
||||
},
|
||||
"current_power_usage_l3": {
|
||||
"name": "Current power usage L3"
|
||||
},
|
||||
"current_power_return_l1": {
|
||||
"name": "Current power return L1"
|
||||
},
|
||||
"current_power_return_l2": {
|
||||
"name": "Current power return L2"
|
||||
},
|
||||
"current_power_return_l3": {
|
||||
"name": "Current power return L3"
|
||||
},
|
||||
"gas_meter_usage": {
|
||||
"name": "Gas meter usage"
|
||||
},
|
||||
"current_voltage_l1": {
|
||||
"name": "Current voltage L1"
|
||||
},
|
||||
"current_voltage_l2": {
|
||||
"name": "Current voltage L2"
|
||||
},
|
||||
"current_voltage_l3": {
|
||||
"name": "Current voltage L3"
|
||||
},
|
||||
"phase_power_current_l1": {
|
||||
"name": "Phase power current L1"
|
||||
},
|
||||
"phase_power_current_l2": {
|
||||
"name": "Phase power current L2"
|
||||
},
|
||||
"phase_power_current_l3": {
|
||||
"name": "Phase power current L3"
|
||||
},
|
||||
"telegram_timestamp": {
|
||||
"name": "Telegram timestamp"
|
||||
},
|
||||
"gas_usage": {
|
||||
"name": "Gas usage"
|
||||
},
|
||||
"current_gas_usage": {
|
||||
"name": "Current gas usage"
|
||||
},
|
||||
"gas_meter_read": {
|
||||
"name": "Gas meter read"
|
||||
},
|
||||
"daily_low_tariff_usage": {
|
||||
"name": "Low tariff usage (daily)"
|
||||
},
|
||||
"daily_high_tariff_usage": {
|
||||
"name": "High tariff usage (daily)"
|
||||
},
|
||||
"daily_low_tariff_return": {
|
||||
"name": "Low tariff return (daily)"
|
||||
},
|
||||
"daily_high_tariff_return": {
|
||||
"name": "High tariff return (daily)"
|
||||
},
|
||||
"daily_power_usage_total": {
|
||||
"name": "Power usage total (daily)"
|
||||
},
|
||||
"daily_power_return_total": {
|
||||
"name": "Power return total (daily)"
|
||||
},
|
||||
"daily_low_tariff_cost": {
|
||||
"name": "Low tariff cost (daily)"
|
||||
},
|
||||
"daily_high_tariff_cost": {
|
||||
"name": "High tariff cost (daily)"
|
||||
},
|
||||
"daily_power_total_cost": {
|
||||
"name": "Power total cost (daily)"
|
||||
},
|
||||
"daily_gas_usage": {
|
||||
"name": "Gas usage (daily)"
|
||||
},
|
||||
"gas_cost": {
|
||||
"name": "Gas cost"
|
||||
},
|
||||
"total_cost": {
|
||||
"name": "Total cost"
|
||||
},
|
||||
"low_tariff_delivered_price": {
|
||||
"name": "Low tariff delivered price"
|
||||
},
|
||||
"high_tariff_delivered_price": {
|
||||
"name": "High tariff delivered price"
|
||||
},
|
||||
"low_tariff_returned_price": {
|
||||
"name": "Low tariff returned price"
|
||||
},
|
||||
"high_tariff_returned_price": {
|
||||
"name": "High tariff returned price"
|
||||
},
|
||||
"gas_price": {
|
||||
"name": "Gas Price"
|
||||
},
|
||||
"current_day_fixed_cost": {
|
||||
"name": "Current day fixed cost"
|
||||
},
|
||||
"dsmr_version": {
|
||||
"name": "DSMR version"
|
||||
},
|
||||
"electricity_tariff": {
|
||||
"name": "Electricity tariff",
|
||||
"state": {
|
||||
"low": "Low",
|
||||
"high": "High"
|
||||
}
|
||||
},
|
||||
"power_failure_count": {
|
||||
"name": "Power failure count"
|
||||
},
|
||||
"long_power_failure_count": {
|
||||
"name": "Long power failure count"
|
||||
},
|
||||
"voltage_sag_l1": {
|
||||
"name": "Voltage sag L1"
|
||||
},
|
||||
"voltage_sag_l2": {
|
||||
"name": "Voltage sag L2"
|
||||
},
|
||||
"voltage_sag_l3": {
|
||||
"name": "Voltage sag L3"
|
||||
},
|
||||
"voltage_swell_l1": {
|
||||
"name": "Voltage swell L1"
|
||||
},
|
||||
"voltage_swell_l2": {
|
||||
"name": "Voltage swell L2"
|
||||
},
|
||||
"voltage_swell_l3": {
|
||||
"name": "Voltage swell L3"
|
||||
},
|
||||
"rejected_telegrams": {
|
||||
"name": "Rejected telegrams"
|
||||
},
|
||||
"current_month_low_tariff_usage": {
|
||||
"name": "Current month low tariff usage"
|
||||
},
|
||||
"current_month_high_tariff_usage": {
|
||||
"name": "Current month high tariff usage"
|
||||
},
|
||||
"current_month_low_tariff_returned": {
|
||||
"name": "Current month low tariff returned"
|
||||
},
|
||||
"current_month_high_tariff_returned": {
|
||||
"name": "Current month high tariff returned"
|
||||
},
|
||||
"current_month_power_usage_total": {
|
||||
"name": "Current month power usage total"
|
||||
},
|
||||
"current_month_power_return_total": {
|
||||
"name": "Current month power return total"
|
||||
},
|
||||
"current_month_low_tariff_cost": {
|
||||
"name": "Current month low tariff cost"
|
||||
},
|
||||
"current_month_high_tariff_cost": {
|
||||
"name": "Current month high tariff cost"
|
||||
},
|
||||
"current_month_power_total_cost": {
|
||||
"name": "Current month power total cost"
|
||||
},
|
||||
"current_month_gas_usage": {
|
||||
"name": "Current month gas usage"
|
||||
},
|
||||
"current_month_gas_cost": {
|
||||
"name": "Current month gas cost"
|
||||
},
|
||||
"current_month_fixed_cost": {
|
||||
"name": "Current month fixed cost"
|
||||
},
|
||||
"current_month_total_cost": {
|
||||
"name": "Current month total cost"
|
||||
},
|
||||
"current_year_low_tariff_usage": {
|
||||
"name": "Current year low tariff usage"
|
||||
},
|
||||
"current_year_high_tariff_usage": {
|
||||
"name": "Current year high tariff usage"
|
||||
},
|
||||
"current_year_low_tariff_returned": {
|
||||
"name": "Current year low tariff returned"
|
||||
},
|
||||
"current_year_high_tariff_returned": {
|
||||
"name": "Current year high tariff returned"
|
||||
},
|
||||
"current_year_power_usage_total": {
|
||||
"name": "Current year power usage total"
|
||||
},
|
||||
"current_year_power_returned_total": {
|
||||
"name": "Current year power returned total"
|
||||
},
|
||||
"current_year_low_tariff_cost": {
|
||||
"name": "Current year low tariff cost"
|
||||
},
|
||||
"current_year_high_tariff_cost": {
|
||||
"name": "Current year high tariff cost"
|
||||
},
|
||||
"current_year_power_total_cost": {
|
||||
"name": "Current year power total cost"
|
||||
},
|
||||
"current_year_gas_usage": {
|
||||
"name": "Current year gas usage"
|
||||
},
|
||||
"current_year_gas_cost": {
|
||||
"name": "Current year gas cost"
|
||||
},
|
||||
"current_year_fixed_cost": {
|
||||
"name": "Current year fixed cost"
|
||||
},
|
||||
"current_year_total_cost": {
|
||||
"name": "Current year total cost"
|
||||
},
|
||||
"previous_quarter_hour_peak_usage": {
|
||||
"name": "Previous quarter-hour peak usage"
|
||||
},
|
||||
"quarter_hour_peak_start_time": {
|
||||
"name": "Quarter-hour peak start time"
|
||||
},
|
||||
"quarter_hour_peak_end_time": {
|
||||
"name": "Quarter-hour peak end time"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ async def async_setup_platform(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml",
|
||||
breaks_in_ha_version="2023.2.0",
|
||||
breaks_in_ha_version="2023.6.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/environment_canada",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["env_canada"],
|
||||
"requirements": ["env_canada==0.5.29"]
|
||||
"requirements": ["env_canada==0.5.30"]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aioesphomeapi", "noiseprotocol"],
|
||||
"requirements": ["aioesphomeapi==13.6.0", "esphome-dashboard-api==1.2.3"],
|
||||
"requirements": ["aioesphomeapi==13.6.1", "esphome-dashboard-api==1.2.3"],
|
||||
"zeroconf": ["_esphomelib._tcp.local."]
|
||||
}
|
||||
|
||||
@@ -38,14 +38,14 @@ class FritzBinarySensorEntityDescription(
|
||||
SENSOR_TYPES: tuple[FritzBinarySensorEntityDescription, ...] = (
|
||||
FritzBinarySensorEntityDescription(
|
||||
key="is_connected",
|
||||
name="Connection",
|
||||
translation_key="is_connected",
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=lambda status, _: bool(status.is_connected),
|
||||
),
|
||||
FritzBinarySensorEntityDescription(
|
||||
key="is_linked",
|
||||
name="Link",
|
||||
translation_key="is_linked",
|
||||
device_class=BinarySensorDeviceClass.PLUG,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=lambda status, _: bool(status.is_linked),
|
||||
|
||||
@@ -39,28 +39,28 @@ class FritzButtonDescription(ButtonEntityDescription, FritzButtonDescriptionMixi
|
||||
BUTTONS: Final = [
|
||||
FritzButtonDescription(
|
||||
key="firmware_update",
|
||||
name="Firmware Update",
|
||||
translation_key="firmware_update",
|
||||
device_class=ButtonDeviceClass.UPDATE,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_action=lambda avm_wrapper: avm_wrapper.async_trigger_firmware_update(),
|
||||
),
|
||||
FritzButtonDescription(
|
||||
key="reboot",
|
||||
name="Reboot",
|
||||
translation_key="reboot",
|
||||
device_class=ButtonDeviceClass.RESTART,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_action=lambda avm_wrapper: avm_wrapper.async_trigger_reboot(),
|
||||
),
|
||||
FritzButtonDescription(
|
||||
key="reconnect",
|
||||
name="Reconnect",
|
||||
translation_key="reconnect",
|
||||
device_class=ButtonDeviceClass.RESTART,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_action=lambda avm_wrapper: avm_wrapper.async_trigger_reconnect(),
|
||||
),
|
||||
FritzButtonDescription(
|
||||
key="cleanup",
|
||||
name="Cleanup",
|
||||
translation_key="cleanup",
|
||||
icon="mdi:broom",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_action=lambda avm_wrapper: avm_wrapper.async_trigger_cleanup(),
|
||||
@@ -86,6 +86,7 @@ class FritzButton(ButtonEntity):
|
||||
"""Defines a Fritz!Box base button."""
|
||||
|
||||
entity_description: FritzButtonDescription
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -97,11 +98,11 @@ class FritzButton(ButtonEntity):
|
||||
self.entity_description = description
|
||||
self.avm_wrapper = avm_wrapper
|
||||
|
||||
self._attr_name = f"{device_friendly_name} {description.name}"
|
||||
self._attr_unique_id = f"{self.avm_wrapper.unique_id}-{description.key}"
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, avm_wrapper.mac)}
|
||||
connections={(CONNECTION_NETWORK_MAC, avm_wrapper.mac)},
|
||||
name=device_friendly_name,
|
||||
)
|
||||
|
||||
async def async_press(self) -> None:
|
||||
|
||||
@@ -1043,7 +1043,6 @@ class FritzBoxBaseCoordinatorEntity(update_coordinator.CoordinatorEntity):
|
||||
)
|
||||
self.entity_description = description
|
||||
self._device_name = device_name
|
||||
self._attr_name = description.name
|
||||
self._attr_unique_id = f"{avm_wrapper.unique_id}-{description.key}"
|
||||
|
||||
@property
|
||||
|
||||
@@ -152,20 +152,20 @@ class FritzSensorEntityDescription(SensorEntityDescription, FritzEntityDescripti
|
||||
SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
FritzSensorEntityDescription(
|
||||
key="external_ip",
|
||||
name="External IP",
|
||||
translation_key="external_ip",
|
||||
icon="mdi:earth",
|
||||
value_fn=_retrieve_external_ip_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="external_ipv6",
|
||||
name="External IPv6",
|
||||
translation_key="external_ipv6",
|
||||
icon="mdi:earth",
|
||||
value_fn=_retrieve_external_ipv6_state,
|
||||
is_suitable=lambda info: info.ipv6_active,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="device_uptime",
|
||||
name="Device Uptime",
|
||||
translation_key="device_uptime",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=_retrieve_device_uptime_state,
|
||||
@@ -173,14 +173,14 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="connection_uptime",
|
||||
name="Connection Uptime",
|
||||
translation_key="connection_uptime",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=_retrieve_connection_uptime_state,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="kb_s_sent",
|
||||
name="Upload Throughput",
|
||||
translation_key="kb_s_sent",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
@@ -189,7 +189,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="kb_s_received",
|
||||
name="Download Throughput",
|
||||
translation_key="kb_s_received",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
@@ -198,7 +198,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="max_kb_s_sent",
|
||||
name="Max Connection Upload Throughput",
|
||||
translation_key="max_kb_s_sent",
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
icon="mdi:upload",
|
||||
@@ -207,7 +207,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="max_kb_s_received",
|
||||
name="Max Connection Download Throughput",
|
||||
translation_key="max_kb_s_received",
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
icon="mdi:download",
|
||||
@@ -216,7 +216,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="gb_sent",
|
||||
name="GB sent",
|
||||
translation_key="gb_sent",
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
native_unit_of_measurement=UnitOfInformation.GIGABYTES,
|
||||
device_class=SensorDeviceClass.DATA_SIZE,
|
||||
@@ -225,7 +225,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="gb_received",
|
||||
name="GB received",
|
||||
translation_key="gb_received",
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
native_unit_of_measurement=UnitOfInformation.GIGABYTES,
|
||||
device_class=SensorDeviceClass.DATA_SIZE,
|
||||
@@ -234,7 +234,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="link_kb_s_sent",
|
||||
name="Link Upload Throughput",
|
||||
translation_key="link_kb_s_sent",
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
icon="mdi:upload",
|
||||
@@ -242,7 +242,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="link_kb_s_received",
|
||||
name="Link Download Throughput",
|
||||
translation_key="link_kb_s_received",
|
||||
native_unit_of_measurement=UnitOfDataRate.KILOBITS_PER_SECOND,
|
||||
device_class=SensorDeviceClass.DATA_RATE,
|
||||
icon="mdi:download",
|
||||
@@ -250,7 +250,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="link_noise_margin_sent",
|
||||
name="Link Upload Noise Margin",
|
||||
translation_key="link_noise_margin_sent",
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
icon="mdi:upload",
|
||||
value_fn=_retrieve_link_noise_margin_sent_state,
|
||||
@@ -258,7 +258,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="link_noise_margin_received",
|
||||
name="Link Download Noise Margin",
|
||||
translation_key="link_noise_margin_received",
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
icon="mdi:download",
|
||||
value_fn=_retrieve_link_noise_margin_received_state,
|
||||
@@ -266,7 +266,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="link_attenuation_sent",
|
||||
name="Link Upload Power Attenuation",
|
||||
translation_key="link_attenuation_sent",
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
icon="mdi:upload",
|
||||
value_fn=_retrieve_link_attenuation_sent_state,
|
||||
@@ -274,7 +274,7 @@ SENSOR_TYPES: tuple[FritzSensorEntityDescription, ...] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="link_attenuation_received",
|
||||
name="Link Download Power Attenuation",
|
||||
translation_key="link_attenuation_received",
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
icon="mdi:download",
|
||||
value_fn=_retrieve_link_attenuation_received_state,
|
||||
|
||||
@@ -52,5 +52,39 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"is_connected": { "name": "Connection" },
|
||||
"is_linked": { "name": "Link" }
|
||||
},
|
||||
"button": {
|
||||
"cleanup": { "name": "Cleanup" },
|
||||
"firmware_update": { "name": "Firmware update" },
|
||||
"reboot": {
|
||||
"name": "[%key:component::button::entity_component::restart::name%]"
|
||||
},
|
||||
"reconnect": { "name": "Reconnect" }
|
||||
},
|
||||
"sensor": {
|
||||
"connection_uptime": { "name": "Connection uptime" },
|
||||
"device_uptime": { "name": "Last restart" },
|
||||
"external_ip": { "name": "External IP" },
|
||||
"external_ipv6": { "name": "External IPv6" },
|
||||
"gb_received": { "name": "GB received" },
|
||||
"gb_sent": { "name": "GB sent" },
|
||||
"kb_s_received": { "name": "Download throughput" },
|
||||
"kb_s_sent": { "name": "Upload throughput" },
|
||||
"link_attenuation_received": {
|
||||
"name": "Link download power attenuation"
|
||||
},
|
||||
"link_attenuation_sent": { "name": "Link upload power attenuation" },
|
||||
"link_kb_s_received": { "name": "Link download throughput" },
|
||||
"link_kb_s_sent": { "name": "Link upload throughput" },
|
||||
"link_noise_margin_received": { "name": "Link download noise margin" },
|
||||
"link_noise_margin_sent": { "name": "Link upload noise margin" },
|
||||
"max_kb_s_received": { "name": "Max connection download throughput" },
|
||||
"max_kb_s_sent": { "name": "Max connection upload throughput" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,8 +113,8 @@ class FritzBoxEntity(CoordinatorEntity[FritzboxDataUpdateCoordinator], ABC):
|
||||
|
||||
self.ain = ain
|
||||
if entity_description is not None:
|
||||
self._attr_has_entity_name = True
|
||||
self.entity_description = entity_description
|
||||
self._attr_name = f"{self.data.name} {entity_description.name}"
|
||||
self._attr_unique_id = f"{ain}_{entity_description.key}"
|
||||
else:
|
||||
self._attr_name = self.data.name
|
||||
|
||||
@@ -40,14 +40,14 @@ class FritzBinarySensorEntityDescription(
|
||||
BINARY_SENSOR_TYPES: Final[tuple[FritzBinarySensorEntityDescription, ...]] = (
|
||||
FritzBinarySensorEntityDescription(
|
||||
key="alarm",
|
||||
name="Alarm",
|
||||
translation_key="alarm",
|
||||
device_class=BinarySensorDeviceClass.WINDOW,
|
||||
suitable=lambda device: device.has_alarm, # type: ignore[no-any-return]
|
||||
is_on=lambda device: device.alert_state, # type: ignore[no-any-return]
|
||||
),
|
||||
FritzBinarySensorEntityDescription(
|
||||
key="lock",
|
||||
name="Button Lock on Device",
|
||||
translation_key="lock",
|
||||
device_class=BinarySensorDeviceClass.LOCK,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
suitable=lambda device: device.lock is not None,
|
||||
@@ -55,7 +55,7 @@ BINARY_SENSOR_TYPES: Final[tuple[FritzBinarySensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzBinarySensorEntityDescription(
|
||||
key="device_lock",
|
||||
name="Button Lock via UI",
|
||||
translation_key="device_lock",
|
||||
device_class=BinarySensorDeviceClass.LOCK,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
suitable=lambda device: device.device_lock is not None,
|
||||
@@ -87,17 +87,6 @@ class FritzboxBinarySensor(FritzBoxDeviceEntity, BinarySensorEntity):
|
||||
|
||||
entity_description: FritzBinarySensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: FritzboxDataUpdateCoordinator,
|
||||
ain: str,
|
||||
entity_description: FritzBinarySensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the FritzBox entity."""
|
||||
super().__init__(coordinator, ain, entity_description)
|
||||
self._attr_name = f"{self.data.name} {entity_description.name}"
|
||||
self._attr_unique_id = f"{ain}_{entity_description.key}"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return true if sensor is on."""
|
||||
|
||||
@@ -91,7 +91,7 @@ def value_scheduled_preset(device: FritzhomeDevice) -> str:
|
||||
SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
FritzSensorEntityDescription(
|
||||
key="temperature",
|
||||
name="Temperature",
|
||||
translation_key="temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -101,7 +101,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="humidity",
|
||||
name="Humidity",
|
||||
translation_key="humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -110,7 +110,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="battery",
|
||||
name="Battery",
|
||||
translation_key="battery",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.BATTERY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -119,7 +119,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="power_consumption",
|
||||
name="Power Consumption",
|
||||
translation_key="power_consumption",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -128,7 +128,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="voltage",
|
||||
name="Voltage",
|
||||
translation_key="voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -137,7 +137,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="electric_current",
|
||||
name="Electric Current",
|
||||
translation_key="electric_current",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -146,7 +146,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="total_energy",
|
||||
name="Total Energy",
|
||||
translation_key="total_energy",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
@@ -156,7 +156,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
# Thermostat Sensors
|
||||
FritzSensorEntityDescription(
|
||||
key="comfort_temperature",
|
||||
name="Comfort Temperature",
|
||||
translation_key="comfort_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
suitable=suitable_comfort_temperature,
|
||||
@@ -164,7 +164,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="eco_temperature",
|
||||
name="Eco Temperature",
|
||||
translation_key="eco_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
suitable=suitable_eco_temperature,
|
||||
@@ -172,7 +172,7 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="nextchange_temperature",
|
||||
name="Next Scheduled Temperature",
|
||||
translation_key="nextchange_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
suitable=suitable_nextchange_temperature,
|
||||
@@ -180,20 +180,20 @@ SENSOR_TYPES: Final[tuple[FritzSensorEntityDescription, ...]] = (
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="nextchange_time",
|
||||
name="Next Scheduled Change Time",
|
||||
translation_key="nextchange_time",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
suitable=suitable_nextchange_time,
|
||||
native_value=lambda device: utc_from_timestamp(device.nextchange_endperiod),
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="nextchange_preset",
|
||||
name="Next Scheduled Preset",
|
||||
translation_key="nextchange_preset",
|
||||
suitable=suitable_nextchange_temperature,
|
||||
native_value=value_nextchange_preset,
|
||||
),
|
||||
FritzSensorEntityDescription(
|
||||
key="scheduled_preset",
|
||||
name="Current Scheduled Preset",
|
||||
translation_key="scheduled_preset",
|
||||
suitable=suitable_nextchange_temperature,
|
||||
native_value=value_scheduled_preset,
|
||||
),
|
||||
|
||||
@@ -36,5 +36,41 @@
|
||||
"error": {
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"alarm": { "name": "Alarm" },
|
||||
"device_lock": { "name": "Button lock via UI" },
|
||||
"lock": { "name": "Button lock on device" }
|
||||
},
|
||||
"sensor": {
|
||||
"battery": {
|
||||
"name": "[%key:component::sensor::entity_component::battery::name%]"
|
||||
},
|
||||
"comfort_temperature": { "name": "Comfort temperature" },
|
||||
"eco_temperature": { "name": "Eco temperature" },
|
||||
"electric_current": {
|
||||
"name": "[%key:component::sensor::entity_component::current::name%]"
|
||||
},
|
||||
"humidity": {
|
||||
"name": "[%key:component::sensor::entity_component::humidity::name%]"
|
||||
},
|
||||
"nextchange_preset": { "name": "Next scheduled preset" },
|
||||
"nextchange_temperature": { "name": "Next scheduled temperature" },
|
||||
"nextchange_time": { "name": "Next scheduled change time" },
|
||||
"power_consumption": {
|
||||
"name": "[%key:component::sensor::entity_component::power::name%]"
|
||||
},
|
||||
"scheduled_preset": { "name": "Current scheduled preset" },
|
||||
"temperature": {
|
||||
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"total_energy": {
|
||||
"name": "[%key:component::sensor::entity_component::energy::name%]"
|
||||
},
|
||||
"voltage": {
|
||||
"name": "[%key:component::sensor::entity_component::voltage::name%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,28 +89,24 @@ class FroniusSensorEntityDescription(SensorEntityDescription):
|
||||
INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_day",
|
||||
name="Energy day",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_year",
|
||||
name="Energy year",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_total",
|
||||
name="Energy total",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="frequency_ac",
|
||||
name="Frequency AC",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
@@ -119,7 +115,6 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="current_ac",
|
||||
name="Current AC",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
@@ -127,7 +122,6 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="current_dc",
|
||||
name="Current DC",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
@@ -136,7 +130,6 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="current_dc_2",
|
||||
name="Current DC 2",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
@@ -145,7 +138,6 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_ac",
|
||||
name="Power AC",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
@@ -153,7 +145,6 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_ac",
|
||||
name="Voltage AC",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
@@ -162,7 +153,6 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_dc",
|
||||
name="Voltage DC",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
@@ -171,7 +161,6 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_dc_2",
|
||||
name="Voltage DC 2",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
@@ -181,28 +170,23 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
# device status entities
|
||||
FroniusSensorEntityDescription(
|
||||
key="inverter_state",
|
||||
name="Inverter state",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="error_code",
|
||||
name="Error code",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="status_code",
|
||||
name="Status code",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="led_state",
|
||||
name="LED state",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="led_color",
|
||||
name="LED color",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
@@ -211,19 +195,16 @@ INVERTER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
LOGGER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
FroniusSensorEntityDescription(
|
||||
key="co2_factor",
|
||||
name="CO₂ factor",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
icon="mdi:molecule-co2",
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="cash_factor",
|
||||
name="Grid export tariff",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
icon="mdi:cash-plus",
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="delivery_factor",
|
||||
name="Grid import tariff",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
icon="mdi:cash-minus",
|
||||
),
|
||||
@@ -232,7 +213,6 @@ LOGGER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
FroniusSensorEntityDescription(
|
||||
key="current_ac_phase_1",
|
||||
name="Current AC phase 1",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -240,7 +220,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="current_ac_phase_2",
|
||||
name="Current AC phase 2",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -248,7 +227,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="current_ac_phase_3",
|
||||
name="Current AC phase 3",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -256,7 +234,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_reactive_ac_consumed",
|
||||
name="Energy reactive AC consumed",
|
||||
native_unit_of_measurement=ENERGY_VOLT_AMPERE_REACTIVE_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
icon="mdi:lightning-bolt-outline",
|
||||
@@ -264,7 +241,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_reactive_ac_produced",
|
||||
name="Energy reactive AC produced",
|
||||
native_unit_of_measurement=ENERGY_VOLT_AMPERE_REACTIVE_HOUR,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
icon="mdi:lightning-bolt-outline",
|
||||
@@ -272,7 +248,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_real_ac_minus",
|
||||
name="Energy real AC minus",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
@@ -280,7 +255,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_real_ac_plus",
|
||||
name="Energy real AC plus",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
@@ -288,33 +262,28 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_real_consumed",
|
||||
name="Energy real consumed",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_real_produced",
|
||||
name="Energy real produced",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="frequency_phase_average",
|
||||
name="Frequency phase average",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="meter_location",
|
||||
name="Meter location",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_apparent_phase_1",
|
||||
name="Power apparent phase 1",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -323,7 +292,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_apparent_phase_2",
|
||||
name="Power apparent phase 2",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -332,7 +300,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_apparent_phase_3",
|
||||
name="Power apparent phase 3",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -341,7 +308,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_apparent",
|
||||
name="Power apparent",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -350,34 +316,29 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_factor_phase_1",
|
||||
name="Power factor phase 1",
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_factor_phase_2",
|
||||
name="Power factor phase 2",
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_factor_phase_3",
|
||||
name="Power factor phase 3",
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_factor",
|
||||
name="Power factor",
|
||||
device_class=SensorDeviceClass.POWER_FACTOR,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_reactive_phase_1",
|
||||
name="Power reactive phase 1",
|
||||
native_unit_of_measurement=POWER_VOLT_AMPERE_REACTIVE,
|
||||
device_class=SensorDeviceClass.REACTIVE_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -386,7 +347,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_reactive_phase_2",
|
||||
name="Power reactive phase 2",
|
||||
native_unit_of_measurement=POWER_VOLT_AMPERE_REACTIVE,
|
||||
device_class=SensorDeviceClass.REACTIVE_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -395,7 +355,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_reactive_phase_3",
|
||||
name="Power reactive phase 3",
|
||||
native_unit_of_measurement=POWER_VOLT_AMPERE_REACTIVE,
|
||||
device_class=SensorDeviceClass.REACTIVE_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -404,7 +363,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_reactive",
|
||||
name="Power reactive",
|
||||
native_unit_of_measurement=POWER_VOLT_AMPERE_REACTIVE,
|
||||
device_class=SensorDeviceClass.REACTIVE_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -413,7 +371,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_real_phase_1",
|
||||
name="Power real phase 1",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -421,7 +378,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_real_phase_2",
|
||||
name="Power real phase 2",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -429,7 +385,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_real_phase_3",
|
||||
name="Power real phase 3",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -437,14 +392,12 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_real",
|
||||
name="Power real",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_ac_phase_1",
|
||||
name="Voltage AC phase 1",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -452,7 +405,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_ac_phase_2",
|
||||
name="Voltage AC phase 2",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -460,7 +412,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_ac_phase_3",
|
||||
name="Voltage AC phase 3",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -468,7 +419,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_ac_phase_to_phase_12",
|
||||
name="Voltage AC phase 1-2",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -476,7 +426,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_ac_phase_to_phase_23",
|
||||
name="Voltage AC phase 2-3",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -484,7 +433,6 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_ac_phase_to_phase_31",
|
||||
name="Voltage AC phase 3-1",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -495,38 +443,32 @@ METER_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
OHMPILOT_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_real_ac_consumed",
|
||||
name="Energy consumed",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_real_ac",
|
||||
name="Power",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="temperature_channel_1",
|
||||
name="Temperature channel 1",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="error_code",
|
||||
name="Error code",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="state_code",
|
||||
name="State code",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="state_message",
|
||||
name="State message",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
]
|
||||
@@ -534,7 +476,6 @@ OHMPILOT_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_day",
|
||||
name="Energy day",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
@@ -542,7 +483,6 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_year",
|
||||
name="Energy year",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
@@ -550,7 +490,6 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="energy_total",
|
||||
name="Energy total",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
@@ -558,12 +497,10 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="meter_mode",
|
||||
name="Meter mode",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_battery",
|
||||
name="Power battery",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
@@ -571,7 +508,6 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_grid",
|
||||
name="Power grid",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
@@ -579,7 +515,6 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_load",
|
||||
name="Power load",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
@@ -587,7 +522,6 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="power_photovoltaics",
|
||||
name="Power photovoltaics",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
@@ -595,7 +529,6 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="relative_autonomy",
|
||||
name="Relative autonomy",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -603,7 +536,6 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="relative_self_consumption",
|
||||
name="Relative self consumption",
|
||||
default_value=0,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -614,19 +546,16 @@ POWER_FLOW_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
STORAGE_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
FroniusSensorEntityDescription(
|
||||
key="capacity_maximum",
|
||||
name="Capacity maximum",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="capacity_designed",
|
||||
name="Capacity designed",
|
||||
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="current_dc",
|
||||
name="Current DC",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -634,7 +563,6 @@ STORAGE_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_dc",
|
||||
name="Voltage DC",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -642,7 +570,6 @@ STORAGE_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_dc_maximum_cell",
|
||||
name="Voltage DC maximum cell",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -651,7 +578,6 @@ STORAGE_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="voltage_dc_minimum_cell",
|
||||
name="Voltage DC minimum cell",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -660,14 +586,12 @@ STORAGE_ENTITY_DESCRIPTIONS: list[FroniusSensorEntityDescription] = [
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="state_of_charge",
|
||||
name="State of charge",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.BATTERY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
FroniusSensorEntityDescription(
|
||||
key="temperature_cell",
|
||||
name="Temperature cell",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -696,6 +620,7 @@ class _FroniusSensorEntity(CoordinatorEntity["FroniusCoordinatorBase"], SensorEn
|
||||
)
|
||||
self.solar_net_id = solar_net_id
|
||||
self._attr_native_value = self._get_entity_value()
|
||||
self._attr_translation_key = self.entity_description.key
|
||||
|
||||
def _device_data(self) -> dict[str, Any]:
|
||||
"""Extract information for SolarNet device from coordinator data."""
|
||||
|
||||
@@ -21,5 +21,219 @@
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"invalid_host": "[%key:common::config_flow::error::invalid_host%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"energy_day": {
|
||||
"name": "Energy day"
|
||||
},
|
||||
"energy_year": {
|
||||
"name": "Energy year"
|
||||
},
|
||||
"energy_total": {
|
||||
"name": "Total energy"
|
||||
},
|
||||
"frequency_ac": {
|
||||
"name": "[%key:component::sensor::entity_component::frequency::name%]"
|
||||
},
|
||||
"current_ac": {
|
||||
"name": "AC current"
|
||||
},
|
||||
"current_dc": {
|
||||
"name": "DC current"
|
||||
},
|
||||
"current_dc_2": {
|
||||
"name": "DC current 2"
|
||||
},
|
||||
"power_ac": {
|
||||
"name": "AC power"
|
||||
},
|
||||
"voltage_ac": {
|
||||
"name": "AC voltage"
|
||||
},
|
||||
"voltage_dc": {
|
||||
"name": "DC voltage"
|
||||
},
|
||||
"voltage_dc_2": {
|
||||
"name": "DC voltage 2"
|
||||
},
|
||||
"inverter_state": {
|
||||
"name": "Inverter state"
|
||||
},
|
||||
"error_code": {
|
||||
"name": "Error code"
|
||||
},
|
||||
"status_code": {
|
||||
"name": "Status code"
|
||||
},
|
||||
"led_state": {
|
||||
"name": "LED state"
|
||||
},
|
||||
"led_color": {
|
||||
"name": "LED color"
|
||||
},
|
||||
"co2_factor": {
|
||||
"name": "CO₂ factor"
|
||||
},
|
||||
"cash_factor": {
|
||||
"name": "Grid export tariff"
|
||||
},
|
||||
"delivery_factor": {
|
||||
"name": "Grid import tariff"
|
||||
},
|
||||
"current_ac_phase_1": {
|
||||
"name": "Current phase 1"
|
||||
},
|
||||
"current_ac_phase_2": {
|
||||
"name": "Current phase 2"
|
||||
},
|
||||
"current_ac_phase_3": {
|
||||
"name": "Current phase 3"
|
||||
},
|
||||
"energy_reactive_ac_consumed": {
|
||||
"name": "Reactive energy consumed"
|
||||
},
|
||||
"energy_reactive_ac_produced": {
|
||||
"name": "Reactive energy produced"
|
||||
},
|
||||
"energy_real_ac_minus": {
|
||||
"name": "Real energy minus"
|
||||
},
|
||||
"energy_real_ac_plus": {
|
||||
"name": "Real energy plus"
|
||||
},
|
||||
"energy_real_consumed": {
|
||||
"name": "Real energy consumed"
|
||||
},
|
||||
"energy_real_produced": {
|
||||
"name": "Real energy produced"
|
||||
},
|
||||
"frequency_phase_average": {
|
||||
"name": "Frequency phase average"
|
||||
},
|
||||
"meter_location": {
|
||||
"name": "Meter location"
|
||||
},
|
||||
"power_apparent_phase_1": {
|
||||
"name": "Apparent power phase 1"
|
||||
},
|
||||
"power_apparent_phase_2": {
|
||||
"name": "Apparent power phase 2"
|
||||
},
|
||||
"power_apparent_phase_3": {
|
||||
"name": "Apparent power phase 3"
|
||||
},
|
||||
"power_apparent": {
|
||||
"name": "[%key:component::sensor::entity_component::apparent_power::name%]"
|
||||
},
|
||||
"power_factor_phase_1": {
|
||||
"name": "Power factor phase 1"
|
||||
},
|
||||
"power_factor_phase_2": {
|
||||
"name": "Power factor phase 2"
|
||||
},
|
||||
"power_factor_phase_3": {
|
||||
"name": "Power factor phase 3"
|
||||
},
|
||||
"power_factor": {
|
||||
"name": "[%key:component::sensor::entity_component::power_factor::name%]"
|
||||
},
|
||||
"power_reactive_phase_1": {
|
||||
"name": "Reactive power phase 1"
|
||||
},
|
||||
"power_reactive_phase_2": {
|
||||
"name": "Reactive power phase 2"
|
||||
},
|
||||
"power_reactive_phase_3": {
|
||||
"name": "Reactive power phase 3"
|
||||
},
|
||||
"power_reactive": {
|
||||
"name": "Reactive power"
|
||||
},
|
||||
"power_real_phase_1": {
|
||||
"name": "Real power phase 1"
|
||||
},
|
||||
"power_real_phase_2": {
|
||||
"name": "Real power phase 2"
|
||||
},
|
||||
"power_real_phase_3": {
|
||||
"name": "Real power phase 3"
|
||||
},
|
||||
"power_real": {
|
||||
"name": "Real power"
|
||||
},
|
||||
"voltage_ac_phase_1": {
|
||||
"name": "Voltage phase 1"
|
||||
},
|
||||
"voltage_ac_phase_2": {
|
||||
"name": "Voltage phase 2"
|
||||
},
|
||||
"voltage_ac_phase_3": {
|
||||
"name": "Voltage phase 3"
|
||||
},
|
||||
"voltage_ac_phase_to_phase_12": {
|
||||
"name": "Voltage phase 1-2"
|
||||
},
|
||||
"voltage_ac_phase_to_phase_23": {
|
||||
"name": "Voltage phase 2-3"
|
||||
},
|
||||
"voltage_ac_phase_to_phase_31": {
|
||||
"name": "Voltage phase 3-1"
|
||||
},
|
||||
"energy_real_ac_consumed": {
|
||||
"name": "Energy consumed"
|
||||
},
|
||||
"power_real_ac": {
|
||||
"name": "[%key:component::sensor::entity_component::power::name%]"
|
||||
},
|
||||
"temperature_channel_1": {
|
||||
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"state_code": {
|
||||
"name": "State code"
|
||||
},
|
||||
"state_message": {
|
||||
"name": "State message"
|
||||
},
|
||||
"meter_mode": {
|
||||
"name": "Meter mode"
|
||||
},
|
||||
"power_battery": {
|
||||
"name": "Power battery"
|
||||
},
|
||||
"power_grid": {
|
||||
"name": "Power grid"
|
||||
},
|
||||
"power_load": {
|
||||
"name": "Power load"
|
||||
},
|
||||
"power_photovoltaics": {
|
||||
"name": "Power photovoltaics"
|
||||
},
|
||||
"relative_autonomy": {
|
||||
"name": "Relative autonomy"
|
||||
},
|
||||
"relative_self_consumption": {
|
||||
"name": "Relative self consumption"
|
||||
},
|
||||
"capacity_maximum": {
|
||||
"name": "Maximum capacity "
|
||||
},
|
||||
"capacity_designed": {
|
||||
"name": "Designed capacity"
|
||||
},
|
||||
"voltage_dc_maximum_cell": {
|
||||
"name": "Maximum cell voltage"
|
||||
},
|
||||
"voltage_dc_minimum_cell": {
|
||||
"name": "Minimum cell voltage"
|
||||
},
|
||||
"state_of_charge": {
|
||||
"name": "State of charge"
|
||||
},
|
||||
"temperature_cell": {
|
||||
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"integration_type": "system",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20230330.0"]
|
||||
"requirements": ["home-assistant-frontend==20230405.0"]
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ class GiosSensorEntityDescription(SensorEntityDescription, GiosSensorRequiredKey
|
||||
SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_AQI,
|
||||
name="AQI",
|
||||
value=lambda sensors: sensors.aqi.value if sensors.aqi else None,
|
||||
icon="mdi:air-filter",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
@@ -69,35 +68,34 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_C6H6,
|
||||
name="C6H6",
|
||||
value=lambda sensors: sensors.c6h6.value if sensors.c6h6 else None,
|
||||
suggested_display_precision=0,
|
||||
icon="mdi:molecule",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="c6h6",
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_CO,
|
||||
name="CO",
|
||||
value=lambda sensors: sensors.co.value if sensors.co else None,
|
||||
suggested_display_precision=0,
|
||||
icon="mdi:molecule",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="co",
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_NO2,
|
||||
name="NO2",
|
||||
value=lambda sensors: sensors.no2.value if sensors.no2 else None,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="no2",
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_NO2,
|
||||
subkey="index",
|
||||
name="NO2 index",
|
||||
value=lambda sensors: sensors.no2.index if sensors.no2 else None,
|
||||
icon="mdi:molecule",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
@@ -106,17 +104,16 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_O3,
|
||||
name="O3",
|
||||
value=lambda sensors: sensors.o3.value if sensors.o3 else None,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.OZONE,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="o3",
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_O3,
|
||||
subkey="index",
|
||||
name="O3 index",
|
||||
value=lambda sensors: sensors.o3.index if sensors.o3 else None,
|
||||
icon="mdi:molecule",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
@@ -125,17 +122,16 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_PM10,
|
||||
name="PM10",
|
||||
value=lambda sensors: sensors.pm10.value if sensors.pm10 else None,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.PM10,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="pm10",
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_PM10,
|
||||
subkey="index",
|
||||
name="PM10 index",
|
||||
value=lambda sensors: sensors.pm10.index if sensors.pm10 else None,
|
||||
icon="mdi:molecule",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
@@ -144,17 +140,16 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_PM25,
|
||||
name="PM2.5",
|
||||
value=lambda sensors: sensors.pm25.value if sensors.pm25 else None,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.PM25,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="pm25",
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_PM25,
|
||||
subkey="index",
|
||||
name="PM2.5 index",
|
||||
value=lambda sensors: sensors.pm25.index if sensors.pm25 else None,
|
||||
icon="mdi:molecule",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
@@ -163,17 +158,16 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_SO2,
|
||||
name="SO2",
|
||||
value=lambda sensors: sensors.so2.value if sensors.so2 else None,
|
||||
suggested_display_precision=0,
|
||||
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="so2",
|
||||
),
|
||||
GiosSensorEntityDescription(
|
||||
key=ATTR_SO2,
|
||||
subkey="index",
|
||||
name="SO2 index",
|
||||
value=lambda sensors: sensors.so2.index if sensors.so2 else None,
|
||||
icon="mdi:molecule",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"aqi": {
|
||||
"name": "[%key:component::sensor::entity_component::aqi::name%]",
|
||||
"state": {
|
||||
"very_bad": "Very bad",
|
||||
"bad": "Bad",
|
||||
@@ -35,7 +36,17 @@
|
||||
"very_good": "Very good"
|
||||
}
|
||||
},
|
||||
"c6h6": {
|
||||
"name": "Benzene"
|
||||
},
|
||||
"co": {
|
||||
"name": "[%key:component::sensor::entity_component::carbon_monoxide::name%]"
|
||||
},
|
||||
"no2": {
|
||||
"name": "[%key:component::sensor::entity_component::nitrogen_dioxide::name%]"
|
||||
},
|
||||
"no2_index": {
|
||||
"name": "Nitrogen dioxide index",
|
||||
"state": {
|
||||
"very_bad": "[%key:component::gios::entity::sensor::aqi::state::very_bad%]",
|
||||
"bad": "[%key:component::gios::entity::sensor::aqi::state::bad%]",
|
||||
@@ -45,7 +56,11 @@
|
||||
"very_good": "[%key:component::gios::entity::sensor::aqi::state::very_good%]"
|
||||
}
|
||||
},
|
||||
"o3": {
|
||||
"name": "[%key:component::sensor::entity_component::ozone::name%]"
|
||||
},
|
||||
"o3_index": {
|
||||
"name": "Ozone index",
|
||||
"state": {
|
||||
"very_bad": "[%key:component::gios::entity::sensor::aqi::state::very_bad%]",
|
||||
"bad": "[%key:component::gios::entity::sensor::aqi::state::bad%]",
|
||||
@@ -55,7 +70,11 @@
|
||||
"very_good": "[%key:component::gios::entity::sensor::aqi::state::very_good%]"
|
||||
}
|
||||
},
|
||||
"pm10": {
|
||||
"name": "[%key:component::sensor::entity_component::pm10::name%]"
|
||||
},
|
||||
"pm10_index": {
|
||||
"name": "Particulate matter 10 μm index",
|
||||
"state": {
|
||||
"very_bad": "[%key:component::gios::entity::sensor::aqi::state::very_bad%]",
|
||||
"bad": "[%key:component::gios::entity::sensor::aqi::state::bad%]",
|
||||
@@ -65,7 +84,11 @@
|
||||
"very_good": "[%key:component::gios::entity::sensor::aqi::state::very_good%]"
|
||||
}
|
||||
},
|
||||
"pm25": {
|
||||
"name": "[%key:component::sensor::entity_component::pm25::name%]"
|
||||
},
|
||||
"pm25_index": {
|
||||
"name": "Particulate matter 2.5 μm index",
|
||||
"state": {
|
||||
"very_bad": "[%key:component::gios::entity::sensor::aqi::state::very_bad%]",
|
||||
"bad": "[%key:component::gios::entity::sensor::aqi::state::bad%]",
|
||||
@@ -75,7 +98,11 @@
|
||||
"very_good": "[%key:component::gios::entity::sensor::aqi::state::very_good%]"
|
||||
}
|
||||
},
|
||||
"so2": {
|
||||
"name": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]"
|
||||
},
|
||||
"so2_index": {
|
||||
"name": "Sulphur dioxide index",
|
||||
"state": {
|
||||
"very_bad": "[%key:component::gios::entity::sensor::aqi::state::very_bad%]",
|
||||
"bad": "[%key:component::gios::entity::sensor::aqi::state::bad%]",
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/goodwe",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["goodwe"],
|
||||
"requirements": ["goodwe==0.2.29"]
|
||||
"requirements": ["goodwe==0.2.30"]
|
||||
}
|
||||
|
||||
@@ -870,23 +870,25 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
self.hassio.get_os_info(),
|
||||
)
|
||||
|
||||
addons = [
|
||||
addon
|
||||
for addon in self.hass.data[DATA_SUPERVISOR_INFO].get("addons", [])
|
||||
if addon[ATTR_STATE] == ATTR_STARTED
|
||||
all_addons = self.hass.data[DATA_SUPERVISOR_INFO].get("addons", [])
|
||||
started_addons = [
|
||||
addon for addon in all_addons if addon[ATTR_STATE] == ATTR_STARTED
|
||||
]
|
||||
stats_data = await asyncio.gather(
|
||||
*[self._update_addon_stats(addon[ATTR_SLUG]) for addon in addons]
|
||||
*[self._update_addon_stats(addon[ATTR_SLUG]) for addon in started_addons]
|
||||
)
|
||||
self.hass.data[DATA_ADDONS_STATS] = dict(stats_data)
|
||||
self.hass.data[DATA_ADDONS_CHANGELOGS] = dict(
|
||||
await asyncio.gather(
|
||||
*[self._update_addon_changelog(addon[ATTR_SLUG]) for addon in addons]
|
||||
*[
|
||||
self._update_addon_changelog(addon[ATTR_SLUG])
|
||||
for addon in all_addons
|
||||
]
|
||||
)
|
||||
)
|
||||
self.hass.data[DATA_ADDONS_INFO] = dict(
|
||||
await asyncio.gather(
|
||||
*[self._update_addon_info(addon[ATTR_SLUG]) for addon in addons]
|
||||
*[self._update_addon_info(addon[ATTR_SLUG]) for addon in all_addons]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -272,7 +272,8 @@ class HKDevice:
|
||||
self.hass,
|
||||
self.async_update_available_state,
|
||||
timedelta(seconds=BLE_AVAILABILITY_CHECK_INTERVAL),
|
||||
f"HomeKit Controller {self.unique_id} BLE availability check poll",
|
||||
name=f"HomeKit Controller {self.unique_id} BLE availability "
|
||||
"check poll",
|
||||
)
|
||||
)
|
||||
# BLE devices always get an RSSI sensor as well
|
||||
@@ -290,7 +291,7 @@ class HKDevice:
|
||||
self.hass,
|
||||
self.async_request_update,
|
||||
self.pairing.poll_interval,
|
||||
f"HomeKit Controller {self.unique_id} availability check poll",
|
||||
name=f"HomeKit Controller {self.unique_id} availability check poll",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiohomekit", "commentjson"],
|
||||
"requirements": ["aiohomekit==2.6.1"],
|
||||
"requirements": ["aiohomekit==2.6.3"],
|
||||
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import email
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aioimaplib import AUTH, IMAP4_SSL, SELECTED, AioImapException
|
||||
from aioimaplib import AUTH, IMAP4_SSL, NONAUTH, SELECTED, AioImapException
|
||||
import async_timeout
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
@@ -36,10 +36,12 @@ async def connect_to_server(data: Mapping[str, Any]) -> IMAP4_SSL:
|
||||
"""Connect to imap server and return client."""
|
||||
client = IMAP4_SSL(data[CONF_SERVER], data[CONF_PORT])
|
||||
await client.wait_hello_from_server()
|
||||
await client.login(data[CONF_USERNAME], data[CONF_PASSWORD])
|
||||
if client.protocol.state != AUTH:
|
||||
if client.protocol.state == NONAUTH:
|
||||
await client.login(data[CONF_USERNAME], data[CONF_PASSWORD])
|
||||
if client.protocol.state not in {AUTH, SELECTED}:
|
||||
raise InvalidAuth("Invalid username or password")
|
||||
await client.select(data[CONF_FOLDER])
|
||||
if client.protocol.state == AUTH:
|
||||
await client.select(data[CONF_FOLDER])
|
||||
if client.protocol.state != SELECTED:
|
||||
raise InvalidFolder(f"Folder {data[CONF_FOLDER]} is invalid")
|
||||
return client
|
||||
@@ -207,10 +209,9 @@ class ImapDataUpdateCoordinator(DataUpdateCoordinator[int | None]):
|
||||
await self.imap_client.stop_wait_server_push()
|
||||
await self.imap_client.close()
|
||||
await self.imap_client.logout()
|
||||
except (AioImapException, asyncio.TimeoutError) as ex:
|
||||
except (AioImapException, asyncio.TimeoutError):
|
||||
if log_error:
|
||||
self.async_set_update_error(ex)
|
||||
_LOGGER.warning("Error while cleaning up imap connection")
|
||||
_LOGGER.debug("Error while cleaning up imap connection")
|
||||
self.imap_client = None
|
||||
|
||||
async def shutdown(self, *_) -> None:
|
||||
@@ -274,30 +275,30 @@ class ImapPushDataUpdateCoordinator(ImapDataUpdateCoordinator):
|
||||
try:
|
||||
number_of_messages = await self._async_fetch_number_of_messages()
|
||||
except InvalidAuth as ex:
|
||||
await self._cleanup()
|
||||
_LOGGER.warning(
|
||||
"Username or password incorrect, starting reauthentication"
|
||||
)
|
||||
self.config_entry.async_start_reauth(self.hass)
|
||||
self.async_set_update_error(ex)
|
||||
await self._cleanup()
|
||||
await asyncio.sleep(BACKOFF_TIME)
|
||||
except InvalidFolder as ex:
|
||||
_LOGGER.warning("Selected mailbox folder is invalid")
|
||||
await self._cleanup()
|
||||
self.config_entry.async_set_state(
|
||||
self.hass,
|
||||
ConfigEntryState.SETUP_ERROR,
|
||||
"Selected mailbox folder is invalid.",
|
||||
)
|
||||
self.async_set_update_error(ex)
|
||||
await self._cleanup()
|
||||
await asyncio.sleep(BACKOFF_TIME)
|
||||
except (
|
||||
UpdateFailed,
|
||||
AioImapException,
|
||||
asyncio.TimeoutError,
|
||||
) as ex:
|
||||
self.async_set_update_error(ex)
|
||||
await self._cleanup()
|
||||
self.async_set_update_error(ex)
|
||||
await asyncio.sleep(BACKOFF_TIME)
|
||||
continue
|
||||
else:
|
||||
@@ -310,13 +311,11 @@ class ImapPushDataUpdateCoordinator(ImapDataUpdateCoordinator):
|
||||
await idle
|
||||
|
||||
except (AioImapException, asyncio.TimeoutError):
|
||||
_LOGGER.warning(
|
||||
_LOGGER.debug(
|
||||
"Lost %s (will attempt to reconnect after %s s)",
|
||||
self.config_entry.data[CONF_SERVER],
|
||||
BACKOFF_TIME,
|
||||
)
|
||||
self.async_set_update_error(UpdateFailed("Lost connection"))
|
||||
await self._cleanup()
|
||||
await asyncio.sleep(BACKOFF_TIME)
|
||||
|
||||
async def shutdown(self, *_) -> None:
|
||||
|
||||
@@ -142,8 +142,11 @@ class ControllerDevice(ClimateEntity):
|
||||
|
||||
# If mode RAS, or mode master with CtrlZone 13 then can set master temperature,
|
||||
# otherwise the unit determines which zone to use as target. See interface manual p. 8
|
||||
# It appears some systems may have a different numbering system, so will trigger
|
||||
# this if the control zone is > total zones.
|
||||
if (
|
||||
controller.ras_mode == "master" and controller.zone_ctrl == 13
|
||||
controller.ras_mode == "master"
|
||||
and controller.zone_ctrl > controller.zones_total
|
||||
) or controller.ras_mode == "RAS":
|
||||
self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from aiolivisi.const import CAPABILITY_CONFIG
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
ClimateEntity,
|
||||
ClimateEntityFeature,
|
||||
@@ -65,8 +67,6 @@ class LivisiClimate(LivisiEntity, ClimateEntity):
|
||||
_attr_hvac_mode = HVACMode.HEAT
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
_attr_target_temperature_high = MAX_TEMPERATURE
|
||||
_attr_target_temperature_low = MIN_TEMPERATURE
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -83,6 +83,10 @@ class LivisiClimate(LivisiEntity, ClimateEntity):
|
||||
self._temperature_capability = self.capabilities["RoomTemperature"]
|
||||
self._humidity_capability = self.capabilities["RoomHumidity"]
|
||||
|
||||
config = device.get(CAPABILITY_CONFIG, {}).get("RoomSetpoint", {})
|
||||
self._attr_max_temp = config.get("maxTemperature", MAX_TEMPERATURE)
|
||||
self._attr_min_temp = config.get("minTemperature", MIN_TEMPERATURE)
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
response = await self.aio_livisi.async_vrcc_set_temperature(
|
||||
|
||||
@@ -32,42 +32,42 @@ from .const import ATTR_SENSOR_ID, CONF_SENSOR_ID, DOMAIN
|
||||
SENSORS: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key="temperature",
|
||||
name="Temperature",
|
||||
translation_key="temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="humidity",
|
||||
name="Humidity",
|
||||
translation_key="humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="pressure",
|
||||
name="Pressure",
|
||||
translation_key="pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.PA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="pressure_at_sealevel",
|
||||
name="Pressure at sealevel",
|
||||
translation_key="pressure_at_sealevel",
|
||||
native_unit_of_measurement=UnitOfPressure.PA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="P1",
|
||||
name="PM10",
|
||||
translation_key="pm10",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM10,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="P2",
|
||||
name="PM2.5",
|
||||
translation_key="pm25",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM25,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
|
||||
@@ -13,5 +13,25 @@
|
||||
"invalid_sensor": "Sensor not available or invalid",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"humidity": {
|
||||
"name": "[%key:component::sensor::entity_component::humidity::name%]"
|
||||
},
|
||||
"pressure": {
|
||||
"name": "[%key:component::sensor::entity_component::pressure::name%]"
|
||||
},
|
||||
"pressure_at_sealevel": { "name": "Pressure at sealevel" },
|
||||
"pm10": {
|
||||
"name": "[%key:component::sensor::entity_component::pm10::name%]"
|
||||
},
|
||||
"pm25": {
|
||||
"name": "[%key:component::sensor::entity_component::pm25::name%]"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ from homeassistant.const import (
|
||||
STATE_ALARM_TRIGGERED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.event import async_track_point_in_time
|
||||
@@ -285,56 +286,34 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity):
|
||||
|
||||
async def async_alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
if not self._async_validate_code(code, STATE_ALARM_DISARMED):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_DISARMED)
|
||||
self._state = STATE_ALARM_DISARMED
|
||||
self._state_ts = dt_util.utcnow()
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Send arm home command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_HOME
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_HOME)
|
||||
self._async_update_state(STATE_ALARM_ARMED_HOME)
|
||||
|
||||
async def async_alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm away command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_AWAY
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_AWAY)
|
||||
self._async_update_state(STATE_ALARM_ARMED_AWAY)
|
||||
|
||||
async def async_alarm_arm_night(self, code: str | None = None) -> None:
|
||||
"""Send arm night command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_NIGHT
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_NIGHT)
|
||||
self._async_update_state(STATE_ALARM_ARMED_NIGHT)
|
||||
|
||||
async def async_alarm_arm_vacation(self, code: str | None = None) -> None:
|
||||
"""Send arm vacation command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_VACATION
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_VACATION)
|
||||
self._async_update_state(STATE_ALARM_ARMED_VACATION)
|
||||
|
||||
async def async_alarm_arm_custom_bypass(self, code: str | None = None) -> None:
|
||||
"""Send arm custom bypass command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_CUSTOM_BYPASS
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_CUSTOM_BYPASS)
|
||||
self._async_update_state(STATE_ALARM_ARMED_CUSTOM_BYPASS)
|
||||
|
||||
async def async_alarm_trigger(self, code: str | None = None) -> None:
|
||||
@@ -383,18 +362,22 @@ class ManualAlarm(alarm.AlarmControlPanelEntity, RestoreEntity):
|
||||
|
||||
def _async_validate_code(self, code, state):
|
||||
"""Validate given code."""
|
||||
if self._code is None:
|
||||
return True
|
||||
if (
|
||||
state != STATE_ALARM_DISARMED and not self.code_arm_required
|
||||
) or self._code is None:
|
||||
return
|
||||
|
||||
if isinstance(self._code, str):
|
||||
alarm_code = self._code
|
||||
else:
|
||||
alarm_code = self._code.async_render(
|
||||
parse_result=False, from_state=self._state, to_state=state
|
||||
)
|
||||
check = not alarm_code or code == alarm_code
|
||||
if not check:
|
||||
_LOGGER.warning("Invalid code given for %s", state)
|
||||
return check
|
||||
|
||||
if not alarm_code or code == alarm_code:
|
||||
return
|
||||
|
||||
raise HomeAssistantError("Invalid alarm code provided")
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
|
||||
@@ -29,6 +29,7 @@ from homeassistant.const import (
|
||||
STATE_ALARM_TRIGGERED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.event import (
|
||||
@@ -345,56 +346,34 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity):
|
||||
|
||||
async def async_alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
if not self._async_validate_code(code, STATE_ALARM_DISARMED):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_DISARMED)
|
||||
self._state = STATE_ALARM_DISARMED
|
||||
self._state_ts = dt_util.utcnow()
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
async def async_alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Send arm home command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_HOME
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_HOME)
|
||||
self._async_update_state(STATE_ALARM_ARMED_HOME)
|
||||
|
||||
async def async_alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm away command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_AWAY
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_AWAY)
|
||||
self._async_update_state(STATE_ALARM_ARMED_AWAY)
|
||||
|
||||
async def async_alarm_arm_night(self, code: str | None = None) -> None:
|
||||
"""Send arm night command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_NIGHT
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_NIGHT)
|
||||
self._async_update_state(STATE_ALARM_ARMED_NIGHT)
|
||||
|
||||
async def async_alarm_arm_vacation(self, code: str | None = None) -> None:
|
||||
"""Send arm vacation command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_VACATION
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_VACATION)
|
||||
self._async_update_state(STATE_ALARM_ARMED_VACATION)
|
||||
|
||||
async def async_alarm_arm_custom_bypass(self, code: str | None = None) -> None:
|
||||
"""Send arm custom bypass command."""
|
||||
if self.code_arm_required and not self._async_validate_code(
|
||||
code, STATE_ALARM_ARMED_CUSTOM_BYPASS
|
||||
):
|
||||
return
|
||||
|
||||
self._async_validate_code(code, STATE_ALARM_ARMED_CUSTOM_BYPASS)
|
||||
self._async_update_state(STATE_ALARM_ARMED_CUSTOM_BYPASS)
|
||||
|
||||
async def async_alarm_trigger(self, code: str | None = None) -> None:
|
||||
@@ -436,18 +415,22 @@ class ManualMQTTAlarm(alarm.AlarmControlPanelEntity):
|
||||
|
||||
def _async_validate_code(self, code, state):
|
||||
"""Validate given code."""
|
||||
if self._code is None:
|
||||
return True
|
||||
if (
|
||||
state != STATE_ALARM_DISARMED and not self.code_arm_required
|
||||
) or self._code is None:
|
||||
return
|
||||
|
||||
if isinstance(self._code, str):
|
||||
alarm_code = self._code
|
||||
else:
|
||||
alarm_code = self._code.async_render(
|
||||
from_state=self._state, to_state=state, parse_result=False
|
||||
)
|
||||
check = not alarm_code or code == alarm_code
|
||||
if not check:
|
||||
_LOGGER.warning("Invalid code given for %s", state)
|
||||
return check
|
||||
|
||||
if not alarm_code or code == alarm_code:
|
||||
return
|
||||
|
||||
raise HomeAssistantError("Invalid alarm code provided")
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
|
||||
@@ -113,6 +113,7 @@ RELOADABLE_PLATFORMS = [
|
||||
Platform.CAMERA,
|
||||
Platform.CLIMATE,
|
||||
Platform.COVER,
|
||||
Platform.DEVICE_TRACKER,
|
||||
Platform.FAN,
|
||||
Platform.HUMIDIFIER,
|
||||
Platform.LIGHT,
|
||||
|
||||
@@ -23,7 +23,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
RESTART_BUTTON: ButtonEntityDescription = ButtonEntityDescription(
|
||||
key="restart",
|
||||
name="Restart",
|
||||
translation_key="restart",
|
||||
device_class=ButtonDeviceClass.RESTART,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
)
|
||||
|
||||
@@ -89,7 +89,7 @@ class NAMSensorEntityDescription(SensorEntityDescription, NAMSensorRequiredKeysM
|
||||
SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_BME280_HUMIDITY,
|
||||
name="BME280 humidity",
|
||||
translation_key="bme280_humidity",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
@@ -98,7 +98,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_BME280_PRESSURE,
|
||||
name="BME280 pressure",
|
||||
translation_key="bme280_pressure",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=UnitOfPressure.HPA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
@@ -107,7 +107,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_BME280_TEMPERATURE,
|
||||
name="BME280 temperature",
|
||||
translation_key="bme280_temperature",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
@@ -116,7 +116,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_BMP180_PRESSURE,
|
||||
name="BMP180 pressure",
|
||||
translation_key="bmp180_pressure",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=UnitOfPressure.HPA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
@@ -125,7 +125,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_BMP180_TEMPERATURE,
|
||||
name="BMP180 temperature",
|
||||
translation_key="bmp180_temperature",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
@@ -134,7 +134,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_BMP280_PRESSURE,
|
||||
name="BMP280 pressure",
|
||||
translation_key="bmp280_pressure",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=UnitOfPressure.HPA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
@@ -143,7 +143,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_BMP280_TEMPERATURE,
|
||||
name="BMP280 temperature",
|
||||
translation_key="bmp280_temperature",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
@@ -152,7 +152,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_HECA_HUMIDITY,
|
||||
name="HECA humidity",
|
||||
translation_key="heca_humidity",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
@@ -161,7 +161,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_HECA_TEMPERATURE,
|
||||
name="HECA temperature",
|
||||
translation_key="heca_temperature",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
@@ -170,7 +170,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_MHZ14A_CARBON_DIOXIDE,
|
||||
name="MH-Z14A carbon dioxide",
|
||||
translation_key="mhz14a_carbon_dioxide",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
|
||||
device_class=SensorDeviceClass.CO2,
|
||||
@@ -179,22 +179,21 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_PMSX003_CAQI,
|
||||
name="PMSx003 CAQI",
|
||||
translation_key="pmsx003_caqi",
|
||||
icon="mdi:air-filter",
|
||||
value=lambda sensors: sensors.pms_caqi,
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_PMSX003_CAQI_LEVEL,
|
||||
name="PMSx003 CAQI level",
|
||||
translation_key="pmsx003_caqi_level",
|
||||
icon="mdi:air-filter",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["very_low", "low", "medium", "high", "very_high"],
|
||||
translation_key="caqi_level",
|
||||
value=lambda sensors: sensors.pms_caqi_level,
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_PMSX003_P0,
|
||||
name="PMSx003 particulate matter 1.0",
|
||||
translation_key="pmsx003_pm1",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM1,
|
||||
@@ -203,7 +202,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_PMSX003_P1,
|
||||
name="PMSx003 particulate matter 10",
|
||||
translation_key="pmsx003_pm10",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM10,
|
||||
@@ -212,7 +211,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_PMSX003_P2,
|
||||
name="PMSx003 particulate matter 2.5",
|
||||
translation_key="pmsx003_pm25",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM25,
|
||||
@@ -221,22 +220,21 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SDS011_CAQI,
|
||||
name="SDS011 CAQI",
|
||||
translation_key="sds011_caqi",
|
||||
icon="mdi:air-filter",
|
||||
value=lambda sensors: sensors.sds011_caqi,
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SDS011_CAQI_LEVEL,
|
||||
name="SDS011 CAQI level",
|
||||
translation_key="sds011_caqi_level",
|
||||
icon="mdi:air-filter",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["very_low", "low", "medium", "high", "very_high"],
|
||||
translation_key="caqi_level",
|
||||
value=lambda sensors: sensors.sds011_caqi_level,
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SDS011_P1,
|
||||
name="SDS011 particulate matter 10",
|
||||
translation_key="sds011_pm10",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM10,
|
||||
@@ -245,7 +243,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SDS011_P2,
|
||||
name="SDS011 particulate matter 2.5",
|
||||
translation_key="sds011_pm25",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM25,
|
||||
@@ -254,7 +252,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SHT3X_HUMIDITY,
|
||||
name="SHT3X humidity",
|
||||
translation_key="sht3x_humidity",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
@@ -263,7 +261,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SHT3X_TEMPERATURE,
|
||||
name="SHT3X temperature",
|
||||
translation_key="sht3x_temperature",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
@@ -272,22 +270,21 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SPS30_CAQI,
|
||||
name="SPS30 CAQI",
|
||||
translation_key="sps30_caqi",
|
||||
icon="mdi:air-filter",
|
||||
value=lambda sensors: sensors.sps30_caqi,
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SPS30_CAQI_LEVEL,
|
||||
name="SPS30 CAQI level",
|
||||
translation_key="sps30_caqi_level",
|
||||
icon="mdi:air-filter",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["very_low", "low", "medium", "high", "very_high"],
|
||||
translation_key="caqi_level",
|
||||
value=lambda sensors: sensors.sps30_caqi_level,
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SPS30_P0,
|
||||
name="SPS30 particulate matter 1.0",
|
||||
translation_key="sps30_pm1",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM1,
|
||||
@@ -296,7 +293,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SPS30_P1,
|
||||
name="SPS30 particulate matter 10",
|
||||
translation_key="sps30_pm10",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM10,
|
||||
@@ -305,7 +302,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SPS30_P2,
|
||||
name="SPS30 particulate matter 2.5",
|
||||
translation_key="sps30_pm25",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
device_class=SensorDeviceClass.PM25,
|
||||
@@ -314,7 +311,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SPS30_P4,
|
||||
name="SPS30 particulate matter 4.0",
|
||||
translation_key="sps30_pm4",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
icon="mdi:molecule",
|
||||
@@ -323,7 +320,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_DHT22_HUMIDITY,
|
||||
name="DHT22 humidity",
|
||||
translation_key="dht22_humidity",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
@@ -332,7 +329,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_DHT22_TEMPERATURE,
|
||||
name="DHT22 temperature",
|
||||
translation_key="dht22_temperature",
|
||||
suggested_display_precision=1,
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
@@ -341,7 +338,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_SIGNAL_STRENGTH,
|
||||
name="Signal strength",
|
||||
translation_key="signal_strength",
|
||||
suggested_display_precision=0,
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||
@@ -352,7 +349,7 @@ SENSORS: tuple[NAMSensorEntityDescription, ...] = (
|
||||
),
|
||||
NAMSensorEntityDescription(
|
||||
key=ATTR_UPTIME,
|
||||
name="Uptime",
|
||||
translation_key="last_restart",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
entity_registry_enabled_default=False,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
|
||||
@@ -39,8 +39,47 @@
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"button": {
|
||||
"restart": {
|
||||
"name": "[%key:component::button::entity_component::restart::name%]"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"caqi_level": {
|
||||
"bme280_humidity": {
|
||||
"name": "BME280 humidity"
|
||||
},
|
||||
"bme280_pressure": {
|
||||
"name": "BME280 pressure"
|
||||
},
|
||||
"bme280_temperature": {
|
||||
"name": "BME280 temperature"
|
||||
},
|
||||
"bmp180_pressure": {
|
||||
"name": "BMP180 pressure"
|
||||
},
|
||||
"bmp180_temperature": {
|
||||
"name": "BMP180 temperature"
|
||||
},
|
||||
"bmp280_pressure": {
|
||||
"name": "BMP280 pressure"
|
||||
},
|
||||
"bmp280_temperature": {
|
||||
"name": "BMP280 temperature"
|
||||
},
|
||||
"heca_humidity": {
|
||||
"name": "HECA humidity"
|
||||
},
|
||||
"heca_temperature": {
|
||||
"name": "HECA temperature"
|
||||
},
|
||||
"mhz14a_carbon_dioxide": {
|
||||
"name": "MH-Z14A carbon dioxide"
|
||||
},
|
||||
"pmsx003_caqi": {
|
||||
"name": "PMSx003 common air quality index"
|
||||
},
|
||||
"pmsx003_caqi_level": {
|
||||
"name": "PMSx003 common air quality index level",
|
||||
"state": {
|
||||
"very_low": "Very low",
|
||||
"low": "Low",
|
||||
@@ -48,6 +87,77 @@
|
||||
"high": "High",
|
||||
"very_high": "Very high"
|
||||
}
|
||||
},
|
||||
"pmsx003_pm1": {
|
||||
"name": "PMSx003 particulate matter 1 μm"
|
||||
},
|
||||
"pmsx003_pm10": {
|
||||
"name": "PMSx003 particulate matter 10 μm"
|
||||
},
|
||||
"pmsx003_pm25": {
|
||||
"name": "PMSx003 particulate matter 2.5 μm"
|
||||
},
|
||||
"sds011_caqi": {
|
||||
"name": "SDS011 common air quality index"
|
||||
},
|
||||
"sds011_caqi_level": {
|
||||
"name": "SDS011 common air quality index level",
|
||||
"state": {
|
||||
"very_low": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::very_low%]",
|
||||
"low": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::low%]",
|
||||
"medium": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::medium%]",
|
||||
"high": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::high%]",
|
||||
"very_high": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::very_high%]"
|
||||
}
|
||||
},
|
||||
"sds011_pm10": {
|
||||
"name": "SDS011 particulate matter 10 μm"
|
||||
},
|
||||
"sds011_pm25": {
|
||||
"name": "SDS011 particulate matter 2.5 μm"
|
||||
},
|
||||
"sht3x_humidity": {
|
||||
"name": "SHT3X humidity"
|
||||
},
|
||||
"sht3x_temperature": {
|
||||
"name": "SHT3X temperature"
|
||||
},
|
||||
"sps30_caqi": {
|
||||
"name": "SPS30 common air quality index"
|
||||
},
|
||||
"sps30_caqi_level": {
|
||||
"name": "SPS30 common air quality index level",
|
||||
"state": {
|
||||
"very_low": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::very_low%]",
|
||||
"low": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::low%]",
|
||||
"medium": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::medium%]",
|
||||
"high": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::high%]",
|
||||
"very_high": "[%key:component::nam::entity::sensor::pmsx003_caqi_level::state::very_high%]"
|
||||
}
|
||||
},
|
||||
"sps30_pm1": {
|
||||
"name": "SPS30 particulate matter 1 μm"
|
||||
},
|
||||
"sps30_pm10": {
|
||||
"name": "SPS30 particulate matter 10 μm"
|
||||
},
|
||||
"sps30_pm25": {
|
||||
"name": "SPS30 particulate matter 2.5 μm"
|
||||
},
|
||||
"sps30_pm4": {
|
||||
"name": "SPS30 Particulate matter 4 μm"
|
||||
},
|
||||
"dht22_humidity": {
|
||||
"name": "DHT22 humidity"
|
||||
},
|
||||
"dht22_temperature": {
|
||||
"name": "DHT22 temperature"
|
||||
},
|
||||
"signal_strength": {
|
||||
"name": "[%key:component::sensor::entity_component::signal_strength::name%]"
|
||||
},
|
||||
"last_restart": {
|
||||
"name": "Last restart"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class TemperatureSensor(SensorBase):
|
||||
|
||||
_attr_device_class = SensorDeviceClass.TEMPERATURE
|
||||
_attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
|
||||
_attr_name = "Temperature"
|
||||
_attr_translation_key = "temperature"
|
||||
|
||||
@property
|
||||
def native_value(self) -> float:
|
||||
@@ -96,7 +96,7 @@ class HumiditySensor(SensorBase):
|
||||
|
||||
_attr_device_class = SensorDeviceClass.HUMIDITY
|
||||
_attr_native_unit_of_measurement = PERCENTAGE
|
||||
_attr_name = "Humidity"
|
||||
_attr_translation_key = "humidity"
|
||||
|
||||
@property
|
||||
def native_value(self) -> int:
|
||||
|
||||
@@ -98,5 +98,15 @@
|
||||
"title": "Nest Authentication Credentials must be updated",
|
||||
"description": "To improve security and reduce phishing risk Google has deprecated the authentication method used by Home Assistant.\n\n**This requires action by you to resolve** ([more info]({more_info_url}))\n\n1. Visit the integrations page\n1. Click Reconfigure on the Nest integration.\n1. Home Assistant will walk you through the steps to upgrade to Web Authentication.\n\nSee the Nest [integration instructions]({documentation_url}) for troubleshooting information."
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"temperature": {
|
||||
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"humidity": {
|
||||
"name": "[%key:component::sensor::entity_component::humidity::name%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,14 +43,14 @@ SENSORS = (
|
||||
NextDnsBinarySensorEntityDescription[ConnectionStatus](
|
||||
key="this_device_nextdns_connection_status",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
name="This device NextDNS connection status",
|
||||
translation_key="device_connection_status",
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
state=lambda data, _: data.connected,
|
||||
),
|
||||
NextDnsBinarySensorEntityDescription[ConnectionStatus](
|
||||
key="this_device_profile_connection_status",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
name="This device profile connection status",
|
||||
translation_key="device_profile_connection_status",
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
state=lambda data, profile_id: profile_id == data.profile_id,
|
||||
),
|
||||
|
||||
@@ -15,7 +15,7 @@ PARALLEL_UPDATES = 1
|
||||
|
||||
CLEAR_LOGS_BUTTON = ButtonEntityDescription(
|
||||
key="clear_logs",
|
||||
name="Clear logs",
|
||||
translation_key="clear_logs",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
)
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
coordinator_type=ATTR_STATUS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
icon="mdi:dns",
|
||||
name="DNS queries",
|
||||
translation_key="all_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.all_queries,
|
||||
@@ -70,7 +70,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
coordinator_type=ATTR_STATUS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
icon="mdi:dns",
|
||||
name="DNS queries blocked",
|
||||
translation_key="blocked_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.blocked_queries,
|
||||
@@ -80,7 +80,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
coordinator_type=ATTR_STATUS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
icon="mdi:dns",
|
||||
name="DNS queries relayed",
|
||||
translation_key="relayed_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.relayed_queries,
|
||||
@@ -90,7 +90,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
coordinator_type=ATTR_STATUS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
icon="mdi:dns",
|
||||
name="DNS queries blocked ratio",
|
||||
translation_key="blocked_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.blocked_queries_ratio,
|
||||
@@ -101,7 +101,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="DNS-over-HTTPS queries",
|
||||
translation_key="doh_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.doh_queries,
|
||||
@@ -112,7 +112,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="DNS-over-HTTP/3 queries",
|
||||
translation_key="doh3_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.doh3_queries,
|
||||
@@ -123,7 +123,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="DNS-over-TLS queries",
|
||||
translation_key="dot_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.dot_queries,
|
||||
@@ -134,7 +134,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="DNS-over-QUIC queries",
|
||||
translation_key="doq_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.doq_queries,
|
||||
@@ -145,7 +145,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="TCP queries",
|
||||
translation_key="tcp_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.tcp_queries,
|
||||
@@ -156,7 +156,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="UDP queries",
|
||||
translation_key="udp_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.udp_queries,
|
||||
@@ -167,7 +167,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
name="DNS-over-HTTPS queries ratio",
|
||||
translation_key="doh_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.doh_queries_ratio,
|
||||
@@ -178,7 +178,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
name="DNS-over-HTTP/3 queries ratio",
|
||||
translation_key="doh3_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.doh3_queries_ratio,
|
||||
@@ -189,7 +189,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="DNS-over-TLS queries ratio",
|
||||
translation_key="dot_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.dot_queries_ratio,
|
||||
@@ -200,7 +200,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
name="DNS-over-QUIC queries ratio",
|
||||
translation_key="doq_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.doq_queries_ratio,
|
||||
@@ -211,7 +211,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="TCP queries ratio",
|
||||
translation_key="tcp_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.tcp_queries_ratio,
|
||||
@@ -222,7 +222,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:dns",
|
||||
name="UDP queries ratio",
|
||||
translation_key="udp_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.udp_queries_ratio,
|
||||
@@ -233,7 +233,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:lock",
|
||||
name="Encrypted queries",
|
||||
translation_key="encrypted_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.encrypted_queries,
|
||||
@@ -244,7 +244,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:lock-open",
|
||||
name="Unencrypted queries",
|
||||
translation_key="unencrypted_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.unencrypted_queries,
|
||||
@@ -255,7 +255,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:lock",
|
||||
name="Encrypted queries ratio",
|
||||
translation_key="encrypted_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.encrypted_queries_ratio,
|
||||
@@ -266,7 +266,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:ip",
|
||||
name="IPv4 queries",
|
||||
translation_key="ipv4_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.ipv4_queries,
|
||||
@@ -277,7 +277,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:ip",
|
||||
name="IPv6 queries",
|
||||
translation_key="ipv6_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.ipv6_queries,
|
||||
@@ -288,7 +288,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:ip",
|
||||
name="IPv6 queries ratio",
|
||||
translation_key="ipv6_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.ipv6_queries_ratio,
|
||||
@@ -299,7 +299,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:lock-check",
|
||||
name="DNSSEC validated queries",
|
||||
translation_key="validated_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.validated_queries,
|
||||
@@ -310,7 +310,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:lock-alert",
|
||||
name="DNSSEC not validated queries",
|
||||
translation_key="not_validated_queries",
|
||||
native_unit_of_measurement="queries",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
value=lambda data: data.not_validated_queries,
|
||||
@@ -321,7 +321,7 @@ SENSORS: tuple[NextDnsSensorEntityDescription, ...] = (
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:lock-check",
|
||||
name="DNSSEC validated queries ratio",
|
||||
translation_key="validated_queries_ratio",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value=lambda data: data.validated_queries_ratio,
|
||||
|
||||
@@ -25,5 +25,294 @@
|
||||
"info": {
|
||||
"can_reach_server": "Reach server"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"device_connection_status": {
|
||||
"name": "Device connection status"
|
||||
},
|
||||
"device_profile_connection_status": {
|
||||
"name": "Device profile connection status"
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
"clear_logs": {
|
||||
"name": "Clear logs"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"all_queries": {
|
||||
"name": "DNS queries"
|
||||
},
|
||||
"blocked_queries": {
|
||||
"name": "DNS queries blocked"
|
||||
},
|
||||
"blocked_queries_ratio": {
|
||||
"name": "DNS queries blocked ratio"
|
||||
},
|
||||
"doh3_queries": {
|
||||
"name": "DNS-over-HTTP/3 queries"
|
||||
},
|
||||
"doh3_queries_ratio": {
|
||||
"name": "DNS-over-HTTP/3 queries ratio"
|
||||
},
|
||||
"doh_queries": {
|
||||
"name": "DNS-over-HTTPS queries"
|
||||
},
|
||||
"doh_queries_ratio": {
|
||||
"name": "DNS-over-HTTPS queries ratio"
|
||||
},
|
||||
"doq_queries": {
|
||||
"name": "DNS-over-QUIC queries"
|
||||
},
|
||||
"doq_queries_ratio": {
|
||||
"name": "DNS-over-QUIC queries ratio"
|
||||
},
|
||||
"dot_queries": {
|
||||
"name": "DNS-over-TLS queries"
|
||||
},
|
||||
"dot_queries_ratio": {
|
||||
"name": "DNS-over-TLS queries ratio"
|
||||
},
|
||||
"encrypted_queries": {
|
||||
"name": "Encrypted queries"
|
||||
},
|
||||
"encrypted_queries_ratio": {
|
||||
"name": "Encrypted queries ratio"
|
||||
},
|
||||
"ipv4_queries": {
|
||||
"name": "IPv4 queries"
|
||||
},
|
||||
"ipv6_queries": {
|
||||
"name": "IPv6 queries"
|
||||
},
|
||||
"ipv6_queries_ratio": {
|
||||
"name": "IPv6 queries ratio"
|
||||
},
|
||||
"not_validated_queries": {
|
||||
"name": "DNSSEC not validated queries"
|
||||
},
|
||||
"relayed_queries": {
|
||||
"name": "DNS queries relayed"
|
||||
},
|
||||
"tcp_queries": {
|
||||
"name": "TCP queries"
|
||||
},
|
||||
"tcp_queries_ratio": {
|
||||
"name": "TCP queries ratio"
|
||||
},
|
||||
"udp_queries": {
|
||||
"name": "UDP queries"
|
||||
},
|
||||
"udp_queries_ratio": {
|
||||
"name": "UDP queries ratio"
|
||||
},
|
||||
"unencrypted_queries": {
|
||||
"name": "Unencrypted queries"
|
||||
},
|
||||
"validated_queries": {
|
||||
"name": "DNSSEC validated queries"
|
||||
},
|
||||
"validated_queries_ratio": {
|
||||
"name": "DNSSEC validated queries ratio"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"ai_threat_detection": {
|
||||
"name": "AI-Driven threat detection"
|
||||
},
|
||||
"allow_affiliate": {
|
||||
"name": "Allow affiliate & tracking links"
|
||||
},
|
||||
"anonymized_ecs": {
|
||||
"name": "Anonymized EDNS client subnet"
|
||||
},
|
||||
"block_9gag": {
|
||||
"name": "Block 9GAG"
|
||||
},
|
||||
"block_amazon": {
|
||||
"name": "Block Amazon"
|
||||
},
|
||||
"block_blizzard": {
|
||||
"name": "Block Blizzard"
|
||||
},
|
||||
"block_bypass_methods": {
|
||||
"name": "Block bypass methods"
|
||||
},
|
||||
"block_csam": {
|
||||
"name": "Block child sexual abuse material"
|
||||
},
|
||||
"block_dailymotion": {
|
||||
"name": "Block Dailymotion"
|
||||
},
|
||||
"block_dating": {
|
||||
"name": "Block dating"
|
||||
},
|
||||
"block_ddns": {
|
||||
"name": "Block dynamic DNS hostnames"
|
||||
},
|
||||
"block_discord": {
|
||||
"name": "Block Discord"
|
||||
},
|
||||
"block_disguised_trackers": {
|
||||
"name": "Block disguised third-party trackers"
|
||||
},
|
||||
"block_disneyplus": {
|
||||
"name": "Block Disney Plus"
|
||||
},
|
||||
"block_ebay": {
|
||||
"name": "Block eBay"
|
||||
},
|
||||
"block_facebook": {
|
||||
"name": "Block Facebook"
|
||||
},
|
||||
"block_fortnite": {
|
||||
"name": "Block Fortnite"
|
||||
},
|
||||
"block_gambling": {
|
||||
"name": "Block gambling"
|
||||
},
|
||||
"block_hulu": {
|
||||
"name": "Block Hulu"
|
||||
},
|
||||
"block_imgur": {
|
||||
"name": "Block Imgur"
|
||||
},
|
||||
"block_instagram": {
|
||||
"name": "Block Instagram"
|
||||
},
|
||||
"block_leagueoflegends": {
|
||||
"name": "Block League of Legends"
|
||||
},
|
||||
"block_messenger": {
|
||||
"name": "Block Messenger"
|
||||
},
|
||||
"block_minecraft": {
|
||||
"name": "Block Minecraft"
|
||||
},
|
||||
"block_netflix": {
|
||||
"name": "Block Netflix"
|
||||
},
|
||||
"block_nrd": {
|
||||
"name": "Block newly registered domains"
|
||||
},
|
||||
"block_page": {
|
||||
"name": "Block page"
|
||||
},
|
||||
"block_parked_domains": {
|
||||
"name": "Block parked domains"
|
||||
},
|
||||
"block_pinterest": {
|
||||
"name": "Block Pinterest"
|
||||
},
|
||||
"block_piracy": {
|
||||
"name": "Block piracy"
|
||||
},
|
||||
"block_porn": {
|
||||
"name": "Block porn"
|
||||
},
|
||||
"block_primevideo": {
|
||||
"name": "Block Prime Video"
|
||||
},
|
||||
"block_reddit": {
|
||||
"name": "Block Reddit"
|
||||
},
|
||||
"block_roblox": {
|
||||
"name": "Block Roblox"
|
||||
},
|
||||
"block_signal": {
|
||||
"name": "Block Signal"
|
||||
},
|
||||
"block_skype": {
|
||||
"name": "Block Skype"
|
||||
},
|
||||
"block_snapchat": {
|
||||
"name": "Block Snapchat"
|
||||
},
|
||||
"block_social_networks": {
|
||||
"name": "Block social networks"
|
||||
},
|
||||
"block_spotify": {
|
||||
"name": "Block Spotify"
|
||||
},
|
||||
"block_steam": {
|
||||
"name": "Block Steam"
|
||||
},
|
||||
"block_telegram": {
|
||||
"name": "Block Telegram"
|
||||
},
|
||||
"block_tiktok": {
|
||||
"name": "Block TikTok"
|
||||
},
|
||||
"block_tinder": {
|
||||
"name": "Block Tinder"
|
||||
},
|
||||
"block_tumblr": {
|
||||
"name": "Block Tumblr"
|
||||
},
|
||||
"block_twitch": {
|
||||
"name": "Block Twitch"
|
||||
},
|
||||
"block_twitter": {
|
||||
"name": "Block Twitter"
|
||||
},
|
||||
"block_vimeo": {
|
||||
"name": "Block Vimeo"
|
||||
},
|
||||
"block_vk": {
|
||||
"name": "Block VK"
|
||||
},
|
||||
"block_whatsapp": {
|
||||
"name": "Block WhatsApp"
|
||||
},
|
||||
"block_xboxlive": {
|
||||
"name": "Block Xbox Live"
|
||||
},
|
||||
"block_youtube": {
|
||||
"name": "Block YouTube"
|
||||
},
|
||||
"block_zoom": {
|
||||
"name": "Block Zoom"
|
||||
},
|
||||
"cache_boost": {
|
||||
"name": "Cache boost"
|
||||
},
|
||||
"cname_flattening": {
|
||||
"name": "CNAME flattening"
|
||||
},
|
||||
"cryptojacking_protection": {
|
||||
"name": "Cryptojacking protection"
|
||||
},
|
||||
"dga_protection": {
|
||||
"name": "Domain generation algorithms protection"
|
||||
},
|
||||
"dns_rebinding_protection": {
|
||||
"name": "DNS rebinding protection"
|
||||
},
|
||||
"google_safe_browsing": {
|
||||
"name": "Google safe browsing"
|
||||
},
|
||||
"idn_homograph_attacks_protection": {
|
||||
"name": "IDN homograph attacks protection"
|
||||
},
|
||||
"logs": {
|
||||
"name": "Logs"
|
||||
},
|
||||
"safesearch": {
|
||||
"name": "Force SafeSearch"
|
||||
},
|
||||
"threat_intelligence_feeds": {
|
||||
"name": "Threat intelligence feeds"
|
||||
},
|
||||
"typosquatting_protection": {
|
||||
"name": "Typosquatting protection"
|
||||
},
|
||||
"web3": {
|
||||
"name": "Web3"
|
||||
},
|
||||
"youtube_restricted_mode": {
|
||||
"name": "Force YouTube restricted mode"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,156 +41,156 @@ class NextDnsSwitchEntityDescription(
|
||||
SWITCHES = (
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_page",
|
||||
name="Block page",
|
||||
translation_key="block_page",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:web-cancel",
|
||||
state=lambda data: data.block_page,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="cache_boost",
|
||||
name="Cache boost",
|
||||
translation_key="cache_boost",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:memory",
|
||||
state=lambda data: data.cache_boost,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="cname_flattening",
|
||||
name="CNAME flattening",
|
||||
translation_key="cname_flattening",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:tournament",
|
||||
state=lambda data: data.cname_flattening,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="anonymized_ecs",
|
||||
name="Anonymized EDNS client subnet",
|
||||
translation_key="anonymized_ecs",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:incognito",
|
||||
state=lambda data: data.anonymized_ecs,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="logs",
|
||||
name="Logs",
|
||||
translation_key="logs",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:file-document-outline",
|
||||
state=lambda data: data.logs,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="web3",
|
||||
name="Web3",
|
||||
translation_key="web3",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:web",
|
||||
state=lambda data: data.web3,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="allow_affiliate",
|
||||
name="Allow affiliate & tracking links",
|
||||
translation_key="allow_affiliate",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.allow_affiliate,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_disguised_trackers",
|
||||
name="Block disguised third-party trackers",
|
||||
translation_key="block_disguised_trackers",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.block_disguised_trackers,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="ai_threat_detection",
|
||||
name="AI-Driven threat detection",
|
||||
translation_key="ai_threat_detection",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.ai_threat_detection,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_csam",
|
||||
name="Block child sexual abuse material",
|
||||
translation_key="block_csam",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.block_csam,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_ddns",
|
||||
name="Block dynamic DNS hostnames",
|
||||
translation_key="block_ddns",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.block_ddns,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_nrd",
|
||||
name="Block newly registered domains",
|
||||
translation_key="block_nrd",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.block_nrd,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_parked_domains",
|
||||
name="Block parked domains",
|
||||
translation_key="block_parked_domains",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.block_parked_domains,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="cryptojacking_protection",
|
||||
name="Cryptojacking protection",
|
||||
translation_key="cryptojacking_protection",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.cryptojacking_protection,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="dga_protection",
|
||||
name="Domain generation algorithms protection",
|
||||
translation_key="dga_protection",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.dga_protection,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="dns_rebinding_protection",
|
||||
name="DNS rebinding protection",
|
||||
translation_key="dns_rebinding_protection",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:dns",
|
||||
state=lambda data: data.dns_rebinding_protection,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="google_safe_browsing",
|
||||
name="Google safe browsing",
|
||||
translation_key="google_safe_browsing",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:google",
|
||||
state=lambda data: data.google_safe_browsing,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="idn_homograph_attacks_protection",
|
||||
name="IDN homograph attacks protection",
|
||||
translation_key="idn_homograph_attacks_protection",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.idn_homograph_attacks_protection,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="threat_intelligence_feeds",
|
||||
name="Threat intelligence feeds",
|
||||
translation_key="threat_intelligence_feeds",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.threat_intelligence_feeds,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="typosquatting_protection",
|
||||
name="Typosquatting protection",
|
||||
translation_key="typosquatting_protection",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:keyboard-outline",
|
||||
state=lambda data: data.typosquatting_protection,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_bypass_methods",
|
||||
name="Block bypass methods",
|
||||
translation_key="block_bypass_methods",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
state=lambda data: data.block_bypass_methods,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="safesearch",
|
||||
name="Force SafeSearch",
|
||||
translation_key="safesearch",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:search-web",
|
||||
state=lambda data: data.safesearch,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="youtube_restricted_mode",
|
||||
name="Force YouTube restricted mode",
|
||||
translation_key="youtube_restricted_mode",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
icon="mdi:youtube",
|
||||
state=lambda data: data.youtube_restricted_mode,
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_9gag",
|
||||
name="Block 9GAG",
|
||||
translation_key="block_9gag",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:file-gif-box",
|
||||
@@ -198,7 +198,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_amazon",
|
||||
name="Block Amazon",
|
||||
translation_key="block_amazon",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:cart-outline",
|
||||
@@ -206,7 +206,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_blizzard",
|
||||
name="Block Blizzard",
|
||||
translation_key="block_blizzard",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:sword-cross",
|
||||
@@ -214,7 +214,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_dailymotion",
|
||||
name="Block Dailymotion",
|
||||
translation_key="block_dailymotion",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:movie-search-outline",
|
||||
@@ -222,7 +222,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_discord",
|
||||
name="Block Discord",
|
||||
translation_key="block_discord",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:message-text",
|
||||
@@ -230,7 +230,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_disneyplus",
|
||||
name="Block Disney Plus",
|
||||
translation_key="block_disneyplus",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:movie-search-outline",
|
||||
@@ -238,7 +238,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_ebay",
|
||||
name="Block eBay",
|
||||
translation_key="block_ebay",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:basket-outline",
|
||||
@@ -246,7 +246,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_facebook",
|
||||
name="Block Facebook",
|
||||
translation_key="block_facebook",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:facebook",
|
||||
@@ -254,7 +254,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_fortnite",
|
||||
name="Block Fortnite",
|
||||
translation_key="block_fortnite",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:tank",
|
||||
@@ -270,7 +270,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_imgur",
|
||||
name="Block Imgur",
|
||||
translation_key="block_imgur",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:camera-image",
|
||||
@@ -278,7 +278,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_instagram",
|
||||
name="Block Instagram",
|
||||
translation_key="block_instagram",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:instagram",
|
||||
@@ -286,7 +286,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_leagueoflegends",
|
||||
name="Block League of Legends",
|
||||
translation_key="block_leagueoflegends",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:sword",
|
||||
@@ -294,7 +294,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_messenger",
|
||||
name="Block Messenger",
|
||||
translation_key="block_messenger",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:message-text",
|
||||
@@ -302,7 +302,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_minecraft",
|
||||
name="Block Minecraft",
|
||||
translation_key="block_minecraft",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:minecraft",
|
||||
@@ -310,7 +310,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_netflix",
|
||||
name="Block Netflix",
|
||||
translation_key="block_netflix",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:netflix",
|
||||
@@ -318,7 +318,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_pinterest",
|
||||
name="Block Pinterest",
|
||||
translation_key="block_pinterest",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:pinterest",
|
||||
@@ -326,7 +326,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_primevideo",
|
||||
name="Block Prime Video",
|
||||
translation_key="block_primevideo",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:filmstrip",
|
||||
@@ -334,7 +334,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_reddit",
|
||||
name="Block Reddit",
|
||||
translation_key="block_reddit",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:reddit",
|
||||
@@ -342,7 +342,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_roblox",
|
||||
name="Block Roblox",
|
||||
translation_key="block_roblox",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:robot",
|
||||
@@ -350,7 +350,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_signal",
|
||||
name="Block Signal",
|
||||
translation_key="block_signal",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:chat-outline",
|
||||
@@ -358,7 +358,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_skype",
|
||||
name="Block Skype",
|
||||
translation_key="block_skype",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:skype",
|
||||
@@ -366,7 +366,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_snapchat",
|
||||
name="Block Snapchat",
|
||||
translation_key="block_snapchat",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:snapchat",
|
||||
@@ -374,7 +374,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_spotify",
|
||||
name="Block Spotify",
|
||||
translation_key="block_spotify",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:spotify",
|
||||
@@ -382,7 +382,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_steam",
|
||||
name="Block Steam",
|
||||
translation_key="block_steam",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:steam",
|
||||
@@ -390,7 +390,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_telegram",
|
||||
name="Block Telegram",
|
||||
translation_key="block_telegram",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:send-outline",
|
||||
@@ -398,7 +398,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_tiktok",
|
||||
name="Block TikTok",
|
||||
translation_key="block_tiktok",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:music-note",
|
||||
@@ -406,7 +406,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_tinder",
|
||||
name="Block Tinder",
|
||||
translation_key="block_tinder",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:fire",
|
||||
@@ -414,7 +414,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_tumblr",
|
||||
name="Block Tumblr",
|
||||
translation_key="block_tumblr",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:image-outline",
|
||||
@@ -422,7 +422,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_twitch",
|
||||
name="Block Twitch",
|
||||
translation_key="block_twitch",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:twitch",
|
||||
@@ -430,7 +430,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_twitter",
|
||||
name="Block Twitter",
|
||||
translation_key="block_twitter",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:twitter",
|
||||
@@ -438,7 +438,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_vimeo",
|
||||
name="Block Vimeo",
|
||||
translation_key="block_vimeo",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:vimeo",
|
||||
@@ -446,7 +446,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_vk",
|
||||
name="Block VK",
|
||||
translation_key="block_vk",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:power-socket-eu",
|
||||
@@ -454,7 +454,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_whatsapp",
|
||||
name="Block WhatsApp",
|
||||
translation_key="block_whatsapp",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:whatsapp",
|
||||
@@ -462,7 +462,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_xboxlive",
|
||||
name="Block Xbox Live",
|
||||
translation_key="block_xboxlive",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:microsoft-xbox",
|
||||
@@ -470,7 +470,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_youtube",
|
||||
name="Block YouTube",
|
||||
translation_key="block_youtube",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:youtube",
|
||||
@@ -478,7 +478,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_zoom",
|
||||
name="Block Zoom",
|
||||
translation_key="block_zoom",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:video",
|
||||
@@ -486,7 +486,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_dating",
|
||||
name="Block dating",
|
||||
translation_key="block_dating",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:candelabra",
|
||||
@@ -494,7 +494,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_gambling",
|
||||
name="Block gambling",
|
||||
translation_key="block_gambling",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:slot-machine",
|
||||
@@ -502,7 +502,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_piracy",
|
||||
name="Block piracy",
|
||||
translation_key="block_piracy",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:pirate",
|
||||
@@ -510,7 +510,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_porn",
|
||||
name="Block porn",
|
||||
translation_key="block_porn",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:movie-off",
|
||||
@@ -518,7 +518,7 @@ SWITCHES = (
|
||||
),
|
||||
NextDnsSwitchEntityDescription[Settings](
|
||||
key="block_social_networks",
|
||||
name="Block social networks",
|
||||
translation_key="block_social_networks",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
entity_registry_enabled_default=False,
|
||||
icon="mdi:facebook",
|
||||
|
||||
@@ -26,7 +26,11 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers import (
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
issue_registry as ir,
|
||||
)
|
||||
from homeassistant.helpers.network import get_url
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
@@ -152,17 +156,36 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
hass, allow_cloud=False, allow_external=False, allow_ip=True, require_ssl=False
|
||||
)
|
||||
url = f"{hass_url}{webhook_url}"
|
||||
try:
|
||||
async with async_timeout.timeout(10):
|
||||
await hass.async_add_executor_job(
|
||||
_register_webhook, bridge, entry.entry_id, url
|
||||
)
|
||||
except InvalidCredentialsException as err:
|
||||
webhook.async_unregister(hass, entry.entry_id)
|
||||
raise ConfigEntryNotReady(f"Invalid credentials for Bridge: {err}") from err
|
||||
except RequestException as err:
|
||||
webhook.async_unregister(hass, entry.entry_id)
|
||||
raise ConfigEntryNotReady(f"Error communicating with Bridge: {err}") from err
|
||||
|
||||
if hass_url.startswith("https"):
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"https_webhook",
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="https_webhook",
|
||||
translation_placeholders={
|
||||
"base_url": hass_url,
|
||||
"network_link": "https://my.home-assistant.io/redirect/network/",
|
||||
},
|
||||
)
|
||||
else:
|
||||
ir.async_delete_issue(hass, DOMAIN, "https_webhook")
|
||||
|
||||
try:
|
||||
async with async_timeout.timeout(10):
|
||||
await hass.async_add_executor_job(
|
||||
_register_webhook, bridge, entry.entry_id, url
|
||||
)
|
||||
except InvalidCredentialsException as err:
|
||||
webhook.async_unregister(hass, entry.entry_id)
|
||||
raise ConfigEntryNotReady(f"Invalid credentials for Bridge: {err}") from err
|
||||
except RequestException as err:
|
||||
webhook.async_unregister(hass, entry.entry_id)
|
||||
raise ConfigEntryNotReady(
|
||||
f"Error communicating with Bridge: {err}"
|
||||
) from err
|
||||
|
||||
async def _stop_nuki(_: Event):
|
||||
"""Stop and remove the Nuki webhook."""
|
||||
|
||||
@@ -36,7 +36,6 @@ class NukiDoorsensorEntity(NukiEntity[NukiDevice], BinarySensorEntity):
|
||||
"""Representation of a Nuki Lock Doorsensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = "Door sensor"
|
||||
_attr_device_class = BinarySensorDeviceClass.DOOR
|
||||
|
||||
@property
|
||||
|
||||
@@ -71,6 +71,7 @@ class NukiDeviceEntity(NukiEntity[_NukiDeviceT], LockEntity):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_supported_features = LockEntityFeature.OPEN
|
||||
_attr_translation_key = "nuki_lock"
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str | None:
|
||||
|
||||
@@ -29,7 +29,7 @@ class NukiBatterySensor(NukiEntity[NukiDevice], SensorEntity):
|
||||
"""Representation of a Nuki Lock Battery sensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = "Battery"
|
||||
_attr_translation_key = "battery"
|
||||
_attr_native_unit_of_measurement = PERCENTAGE
|
||||
_attr_device_class = SensorDeviceClass.BATTERY
|
||||
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||
|
||||
@@ -25,5 +25,30 @@
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"https_webhook": {
|
||||
"title": "Nuki webhook URL uses HTTPS (SSL)",
|
||||
"description": "The Nuki bridge can not push events to an HTTPS address (SSL), please configure a (local) HTTP address under \"Home Assistant URL\" in the [network settings]({network_link}). The current (local) address is: `{base_url}`, a valid address could, for example, be `http://192.168.1.10:8123` where `192.168.1.10` is the IP of the Home Assistant device"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"lock": {
|
||||
"nuki_lock": {
|
||||
"state_attributes": {
|
||||
"battery_critical": {
|
||||
"state": {
|
||||
"on": "[%key:component::binary_sensor::entity_component::battery::state::on%]",
|
||||
"off": "[%key:component::binary_sensor::entity_component::battery::state::off%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"battery": {
|
||||
"name": "[%key:component::sensor::entity_component::battery::name%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,22 +57,22 @@ _LOGGER = logging.getLogger(__name__)
|
||||
SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
"ups.status.display": SensorEntityDescription(
|
||||
key="ups.status.display",
|
||||
name="Status",
|
||||
translation_key="ups_status_display",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
"ups.status": SensorEntityDescription(
|
||||
key="ups.status",
|
||||
name="Status Data",
|
||||
translation_key="ups_status",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
"ups.alarm": SensorEntityDescription(
|
||||
key="ups.alarm",
|
||||
name="Alarms",
|
||||
translation_key="ups_alarm",
|
||||
icon="mdi:alarm",
|
||||
),
|
||||
"ups.temperature": SensorEntityDescription(
|
||||
key="ups.temperature",
|
||||
name="UPS Temperature",
|
||||
translation_key="ups_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -81,14 +81,14 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.load": SensorEntityDescription(
|
||||
key="ups.load",
|
||||
name="Load",
|
||||
translation_key="ups_load",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"ups.load.high": SensorEntityDescription(
|
||||
key="ups.load.high",
|
||||
name="Overload Setting",
|
||||
translation_key="ups_load_high",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -96,14 +96,14 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.id": SensorEntityDescription(
|
||||
key="ups.id",
|
||||
name="System identifier",
|
||||
translation_key="ups_id",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.delay.start": SensorEntityDescription(
|
||||
key="ups.delay.start",
|
||||
name="Load Restart Delay",
|
||||
translation_key="ups_delay_start",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -111,7 +111,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.delay.reboot": SensorEntityDescription(
|
||||
key="ups.delay.reboot",
|
||||
name="UPS Reboot Delay",
|
||||
translation_key="ups_delay_reboot",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -119,7 +119,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.delay.shutdown": SensorEntityDescription(
|
||||
key="ups.delay.shutdown",
|
||||
name="UPS Shutdown Delay",
|
||||
translation_key="ups_delay_shutdown",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -127,7 +127,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.timer.start": SensorEntityDescription(
|
||||
key="ups.timer.start",
|
||||
name="Load Start Timer",
|
||||
translation_key="ups_timer_start",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -135,7 +135,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.timer.reboot": SensorEntityDescription(
|
||||
key="ups.timer.reboot",
|
||||
name="Load Reboot Timer",
|
||||
translation_key="ups_timer_reboot",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -143,7 +143,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.timer.shutdown": SensorEntityDescription(
|
||||
key="ups.timer.shutdown",
|
||||
name="Load Shutdown Timer",
|
||||
translation_key="ups_timer_shutdown",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -151,7 +151,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.test.interval": SensorEntityDescription(
|
||||
key="ups.test.interval",
|
||||
name="Self-Test Interval",
|
||||
translation_key="ups_test_interval",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -159,35 +159,35 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.test.result": SensorEntityDescription(
|
||||
key="ups.test.result",
|
||||
name="Self-Test Result",
|
||||
translation_key="ups_test_result",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.test.date": SensorEntityDescription(
|
||||
key="ups.test.date",
|
||||
name="Self-Test Date",
|
||||
translation_key="ups_test_date",
|
||||
icon="mdi:calendar",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.display.language": SensorEntityDescription(
|
||||
key="ups.display.language",
|
||||
name="Language",
|
||||
translation_key="ups_display_language",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.contacts": SensorEntityDescription(
|
||||
key="ups.contacts",
|
||||
name="External Contacts",
|
||||
translation_key="ups_contacts",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.efficiency": SensorEntityDescription(
|
||||
key="ups.efficiency",
|
||||
name="Efficiency",
|
||||
translation_key="ups_efficiency",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -196,7 +196,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.power": SensorEntityDescription(
|
||||
key="ups.power",
|
||||
name="Current Apparent Power",
|
||||
translation_key="ups_power",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -205,7 +205,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.power.nominal": SensorEntityDescription(
|
||||
key="ups.power.nominal",
|
||||
name="Nominal Power",
|
||||
translation_key="ups_power_nominal",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -213,7 +213,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.realpower": SensorEntityDescription(
|
||||
key="ups.realpower",
|
||||
name="Current Real Power",
|
||||
translation_key="ups_realpower",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -222,7 +222,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.realpower.nominal": SensorEntityDescription(
|
||||
key="ups.realpower.nominal",
|
||||
name="Nominal Real Power",
|
||||
translation_key="ups_realpower_nominal",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -230,63 +230,63 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ups.beeper.status": SensorEntityDescription(
|
||||
key="ups.beeper.status",
|
||||
name="Beeper Status",
|
||||
translation_key="ups_beeper_status",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.type": SensorEntityDescription(
|
||||
key="ups.type",
|
||||
name="UPS Type",
|
||||
translation_key="ups_type",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.watchdog.status": SensorEntityDescription(
|
||||
key="ups.watchdog.status",
|
||||
name="Watchdog Status",
|
||||
translation_key="ups_watchdog_status",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.start.auto": SensorEntityDescription(
|
||||
key="ups.start.auto",
|
||||
name="Start on AC",
|
||||
translation_key="ups_start_auto",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.start.battery": SensorEntityDescription(
|
||||
key="ups.start.battery",
|
||||
name="Start on Battery",
|
||||
translation_key="ups_start_battery",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.start.reboot": SensorEntityDescription(
|
||||
key="ups.start.reboot",
|
||||
name="Reboot on Battery",
|
||||
translation_key="ups_start_reboot",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"ups.shutdown": SensorEntityDescription(
|
||||
key="ups.shutdown",
|
||||
name="Shutdown Ability",
|
||||
translation_key="ups_shutdown",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"battery.charge": SensorEntityDescription(
|
||||
key="battery.charge",
|
||||
name="Battery Charge",
|
||||
translation_key="battery_charge",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.BATTERY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"battery.charge.low": SensorEntityDescription(
|
||||
key="battery.charge.low",
|
||||
name="Low Battery Setpoint",
|
||||
translation_key="battery_charge_low",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -294,7 +294,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.charge.restart": SensorEntityDescription(
|
||||
key="battery.charge.restart",
|
||||
name="Minimum Battery to Start",
|
||||
translation_key="battery_charge_restart",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -302,7 +302,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.charge.warning": SensorEntityDescription(
|
||||
key="battery.charge.warning",
|
||||
name="Warning Battery Setpoint",
|
||||
translation_key="battery_charge_warning",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:gauge",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -310,12 +310,12 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.charger.status": SensorEntityDescription(
|
||||
key="battery.charger.status",
|
||||
name="Charging Status",
|
||||
translation_key="battery_charger_status",
|
||||
icon="mdi:information-outline",
|
||||
),
|
||||
"battery.voltage": SensorEntityDescription(
|
||||
key="battery.voltage",
|
||||
name="Battery Voltage",
|
||||
translation_key="battery_voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -324,7 +324,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.voltage.nominal": SensorEntityDescription(
|
||||
key="battery.voltage.nominal",
|
||||
name="Nominal Battery Voltage",
|
||||
translation_key="battery_voltage_nominal",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -332,7 +332,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.voltage.low": SensorEntityDescription(
|
||||
key="battery.voltage.low",
|
||||
name="Low Battery Voltage",
|
||||
translation_key="battery_voltage_low",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -340,7 +340,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.voltage.high": SensorEntityDescription(
|
||||
key="battery.voltage.high",
|
||||
name="High Battery Voltage",
|
||||
translation_key="battery_voltage_high",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -348,7 +348,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.capacity": SensorEntityDescription(
|
||||
key="battery.capacity",
|
||||
name="Battery Capacity",
|
||||
translation_key="battery_capacity",
|
||||
native_unit_of_measurement="Ah",
|
||||
icon="mdi:flash",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -356,7 +356,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.current": SensorEntityDescription(
|
||||
key="battery.current",
|
||||
name="Battery Current",
|
||||
translation_key="battery_current",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -365,7 +365,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.current.total": SensorEntityDescription(
|
||||
key="battery.current.total",
|
||||
name="Total Battery Current",
|
||||
translation_key="battery_current_total",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -373,7 +373,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.temperature": SensorEntityDescription(
|
||||
key="battery.temperature",
|
||||
name="Battery Temperature",
|
||||
translation_key="battery_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -382,7 +382,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.runtime": SensorEntityDescription(
|
||||
key="battery.runtime",
|
||||
name="Battery Runtime",
|
||||
translation_key="battery_runtime",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -390,7 +390,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.runtime.low": SensorEntityDescription(
|
||||
key="battery.runtime.low",
|
||||
name="Low Battery Runtime",
|
||||
translation_key="battery_runtime_low",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -398,7 +398,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.runtime.restart": SensorEntityDescription(
|
||||
key="battery.runtime.restart",
|
||||
name="Minimum Battery Runtime to Start",
|
||||
translation_key="battery_runtime_restart",
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -406,56 +406,56 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"battery.alarm.threshold": SensorEntityDescription(
|
||||
key="battery.alarm.threshold",
|
||||
name="Battery Alarm Threshold",
|
||||
translation_key="battery_alarm_threshold",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"battery.date": SensorEntityDescription(
|
||||
key="battery.date",
|
||||
name="Battery Date",
|
||||
translation_key="battery_date",
|
||||
icon="mdi:calendar",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"battery.mfr.date": SensorEntityDescription(
|
||||
key="battery.mfr.date",
|
||||
name="Battery Manuf. Date",
|
||||
translation_key="battery_mfr_date",
|
||||
icon="mdi:calendar",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"battery.packs": SensorEntityDescription(
|
||||
key="battery.packs",
|
||||
name="Number of Batteries",
|
||||
translation_key="battery_packs",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"battery.packs.bad": SensorEntityDescription(
|
||||
key="battery.packs.bad",
|
||||
name="Number of Bad Batteries",
|
||||
translation_key="battery_packs_bad",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"battery.type": SensorEntityDescription(
|
||||
key="battery.type",
|
||||
name="Battery Chemistry",
|
||||
translation_key="battery_type",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"input.sensitivity": SensorEntityDescription(
|
||||
key="input.sensitivity",
|
||||
name="Input Power Sensitivity",
|
||||
translation_key="input_sensitivity",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"input.transfer.low": SensorEntityDescription(
|
||||
key="input.transfer.low",
|
||||
name="Low Voltage Transfer",
|
||||
translation_key="input_transfer_low",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -463,7 +463,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"input.transfer.high": SensorEntityDescription(
|
||||
key="input.transfer.high",
|
||||
name="High Voltage Transfer",
|
||||
translation_key="input_transfer_high",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -471,21 +471,21 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"input.transfer.reason": SensorEntityDescription(
|
||||
key="input.transfer.reason",
|
||||
name="Voltage Transfer Reason",
|
||||
translation_key="input_transfer_reason",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"input.voltage": SensorEntityDescription(
|
||||
key="input.voltage",
|
||||
name="Input Voltage",
|
||||
translation_key="input_voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"input.voltage.nominal": SensorEntityDescription(
|
||||
key="input.voltage.nominal",
|
||||
name="Nominal Input Voltage",
|
||||
translation_key="input_voltage_nominal",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -493,7 +493,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"input.frequency": SensorEntityDescription(
|
||||
key="input.frequency",
|
||||
name="Input Line Frequency",
|
||||
translation_key="input_frequency",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -502,7 +502,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"input.frequency.nominal": SensorEntityDescription(
|
||||
key="input.frequency.nominal",
|
||||
name="Nominal Input Line Frequency",
|
||||
translation_key="input_frequency_nominal",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -510,14 +510,14 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"input.frequency.status": SensorEntityDescription(
|
||||
key="input.frequency.status",
|
||||
name="Input Frequency Status",
|
||||
translation_key="input_frequency_status",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"input.bypass.frequency": SensorEntityDescription(
|
||||
key="input.bypass.frequency",
|
||||
name="Input Bypass Frequency",
|
||||
translation_key="input_bypass_frequency",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -526,14 +526,14 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"input.bypass.phases": SensorEntityDescription(
|
||||
key="input.bypass.phases",
|
||||
name="Input Bypass Phases",
|
||||
translation_key="input_bypass_phases",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"input.current": SensorEntityDescription(
|
||||
key="input.current",
|
||||
name="Input Current",
|
||||
translation_key="input_current",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -542,14 +542,14 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"input.phases": SensorEntityDescription(
|
||||
key="input.phases",
|
||||
name="Input Phases",
|
||||
translation_key="input_phases",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"input.realpower": SensorEntityDescription(
|
||||
key="input.realpower",
|
||||
name="Current Input Real Power",
|
||||
translation_key="input_realpower",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -558,7 +558,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.power.nominal": SensorEntityDescription(
|
||||
key="output.power.nominal",
|
||||
name="Nominal Output Power",
|
||||
translation_key="output_power_nominal",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -566,7 +566,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.current": SensorEntityDescription(
|
||||
key="output.current",
|
||||
name="Output Current",
|
||||
translation_key="output_current",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -575,7 +575,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.current.nominal": SensorEntityDescription(
|
||||
key="output.current.nominal",
|
||||
name="Nominal Output Current",
|
||||
translation_key="output_current_nominal",
|
||||
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
|
||||
device_class=SensorDeviceClass.CURRENT,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -583,14 +583,14 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.voltage": SensorEntityDescription(
|
||||
key="output.voltage",
|
||||
name="Output Voltage",
|
||||
translation_key="output_voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"output.voltage.nominal": SensorEntityDescription(
|
||||
key="output.voltage.nominal",
|
||||
name="Nominal Output Voltage",
|
||||
translation_key="output_voltage_nominal",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -598,7 +598,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.frequency": SensorEntityDescription(
|
||||
key="output.frequency",
|
||||
name="Output Frequency",
|
||||
translation_key="output_frequency",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -607,7 +607,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.frequency.nominal": SensorEntityDescription(
|
||||
key="output.frequency.nominal",
|
||||
name="Nominal Output Frequency",
|
||||
translation_key="output_frequency_nominal",
|
||||
native_unit_of_measurement=UnitOfFrequency.HERTZ,
|
||||
device_class=SensorDeviceClass.FREQUENCY,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -615,14 +615,14 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.phases": SensorEntityDescription(
|
||||
key="output.phases",
|
||||
name="Output Phases",
|
||||
translation_key="output_phases",
|
||||
icon="mdi:information-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"output.power": SensorEntityDescription(
|
||||
key="output.power",
|
||||
name="Output Apparent Power",
|
||||
translation_key="output_power",
|
||||
native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
|
||||
device_class=SensorDeviceClass.APPARENT_POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -631,7 +631,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.realpower": SensorEntityDescription(
|
||||
key="output.realpower",
|
||||
name="Current Output Real Power",
|
||||
translation_key="output_realpower",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -640,7 +640,7 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"output.realpower.nominal": SensorEntityDescription(
|
||||
key="output.realpower.nominal",
|
||||
name="Nominal Output Real Power",
|
||||
translation_key="output_realpower_nominal",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@@ -648,21 +648,21 @@ SENSOR_TYPES: Final[dict[str, SensorEntityDescription]] = {
|
||||
),
|
||||
"ambient.humidity": SensorEntityDescription(
|
||||
key="ambient.humidity",
|
||||
name="Ambient Humidity",
|
||||
translation_key="ambient_humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"ambient.temperature": SensorEntityDescription(
|
||||
key="ambient.temperature",
|
||||
name="Ambient Temperature",
|
||||
translation_key="ambient_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"watts": SensorEntityDescription(
|
||||
key="watts",
|
||||
name="Watts",
|
||||
translation_key="watts",
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -717,6 +717,8 @@ async def async_setup_entry(
|
||||
class NUTSensor(CoordinatorEntity[DataUpdateCoordinator[dict[str, str]]], SensorEntity):
|
||||
"""Representation of a sensor entity for NUT status values."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: DataUpdateCoordinator[dict[str, str]],
|
||||
@@ -729,7 +731,6 @@ class NUTSensor(CoordinatorEntity[DataUpdateCoordinator[dict[str, str]]], Sensor
|
||||
self.entity_description = sensor_description
|
||||
|
||||
device_name = data.name.title()
|
||||
self._attr_name = f"{device_name} {sensor_description.name}"
|
||||
self._attr_unique_id = f"{unique_id}_{sensor_description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
|
||||
@@ -33,5 +33,89 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ambient_humidity": { "name": "Ambient humidity" },
|
||||
"ambient_temperature": { "name": "Ambient temperature" },
|
||||
"battery_alarm_threshold": { "name": "Battery alarm threshold" },
|
||||
"battery_capacity": { "name": "Battery capacity" },
|
||||
"battery_charge": { "name": "Battery charge" },
|
||||
"battery_charge_low": { "name": "Low battery setpoint" },
|
||||
"battery_charge_restart": { "name": "Minimum battery to start" },
|
||||
"battery_charge_warning": { "name": "Warning battery setpoint" },
|
||||
"battery_charger_status": { "name": "Charging status" },
|
||||
"battery_current": { "name": "Battery current" },
|
||||
"battery_current_total": { "name": "Total battery current" },
|
||||
"battery_date": { "name": "Battery date" },
|
||||
"battery_mfr_date": { "name": "Battery manuf. date" },
|
||||
"battery_packs": { "name": "Number of batteries" },
|
||||
"battery_packs_bad": { "name": "Number of bad batteries" },
|
||||
"battery_runtime": { "name": "Battery runtime" },
|
||||
"battery_runtime_low": { "name": "Low battery runtime" },
|
||||
"battery_runtime_restart": { "name": "Minimum battery runtime to start" },
|
||||
"battery_temperature": { "name": "Battery temperature" },
|
||||
"battery_type": { "name": "Battery chemistry" },
|
||||
"battery_voltage": { "name": "Battery voltage" },
|
||||
"battery_voltage_high": { "name": "High battery voltage" },
|
||||
"battery_voltage_low": { "name": "Low battery voltage" },
|
||||
"battery_voltage_nominal": { "name": "Nominal battery voltage" },
|
||||
"input_bypass_frequency": { "name": "Input bypass frequency" },
|
||||
"input_bypass_phases": { "name": "Input bypass phases" },
|
||||
"input_current": { "name": "Input current" },
|
||||
"input_frequency": { "name": "Input line frequency" },
|
||||
"input_frequency_nominal": { "name": "Nominal input line frequency" },
|
||||
"input_frequency_status": { "name": "Input frequency status" },
|
||||
"input_phases": { "name": "Input phases" },
|
||||
"input_realpower": { "name": "Current input real power" },
|
||||
"input_sensitivity": { "name": "Input power sensitivity" },
|
||||
"input_transfer_high": { "name": "High voltage transfer" },
|
||||
"input_transfer_low": { "name": "Low voltage transfer" },
|
||||
"input_transfer_reason": { "name": "Voltage transfer reason" },
|
||||
"input_voltage": { "name": "Input voltage" },
|
||||
"input_voltage_nominal": { "name": "Nominal input voltage" },
|
||||
"output_current": { "name": "Output current" },
|
||||
"output_current_nominal": { "name": "Nominal output current" },
|
||||
"output_frequency": { "name": "Output frequency" },
|
||||
"output_frequency_nominal": { "name": "Nominal output frequency" },
|
||||
"output_phases": { "name": "Output phases" },
|
||||
"output_power": { "name": "Output apparent power" },
|
||||
"output_power_nominal": { "name": "Nominal output power" },
|
||||
"output_realpower": { "name": "Current output real power" },
|
||||
"output_realpower_nominal": { "name": "Nominal output real power" },
|
||||
"output_voltage": { "name": "Output voltage" },
|
||||
"output_voltage_nominal": { "name": "Nominal output voltage" },
|
||||
"ups_alarm": { "name": "Alarms" },
|
||||
"ups_beeper_status": { "name": "Beeper status" },
|
||||
"ups_contacts": { "name": "External contacts" },
|
||||
"ups_delay_reboot": { "name": "UPS reboot delay" },
|
||||
"ups_delay_shutdown": { "name": "UPS shutdown delay" },
|
||||
"ups_delay_start": { "name": "Load restart delay" },
|
||||
"ups_display_language": { "name": "Language" },
|
||||
"ups_efficiency": { "name": "Efficiency" },
|
||||
"ups_id": { "name": "System identifier" },
|
||||
"ups_load": { "name": "Load" },
|
||||
"ups_load_high": { "name": "Overload setting" },
|
||||
"ups_power": { "name": "Current apparent power" },
|
||||
"ups_power_nominal": { "name": "Nominal power" },
|
||||
"ups_realpower": { "name": "Current real power" },
|
||||
"ups_realpower_nominal": { "name": "Nominal real power" },
|
||||
"ups_shutdown": { "name": "Shutdown ability" },
|
||||
"ups_start_auto": { "name": "Start on ac" },
|
||||
"ups_start_battery": { "name": "Start on battery" },
|
||||
"ups_start_reboot": { "name": "Reboot on battery" },
|
||||
"ups_status": { "name": "Status data" },
|
||||
"ups_status_display": { "name": "Status" },
|
||||
"ups_temperature": { "name": "UPS temperature" },
|
||||
"ups_test_date": { "name": "Self-test date" },
|
||||
"ups_test_interval": { "name": "Self-test interval" },
|
||||
"ups_test_result": { "name": "Self-test result" },
|
||||
"ups_timer_reboot": { "name": "Load reboot timer" },
|
||||
"ups_timer_shutdown": { "name": "Load shutdown timer" },
|
||||
"ups_timer_start": { "name": "Load start timer" },
|
||||
"ups_type": { "name": "UPS type" },
|
||||
"ups_watchdog_status": { "name": "Watchdog status" },
|
||||
"watts": { "name": "Watts" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ DEVICE_BINARY_SENSORS: dict[str, tuple[OneWireBinarySensorEntityDescription, ...
|
||||
OneWireBinarySensorEntityDescription(
|
||||
key=f"sensed.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Sensed {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key=f"sensed_{id.lower()}",
|
||||
)
|
||||
for id in DEVICE_KEYS_A_B
|
||||
),
|
||||
@@ -46,8 +46,8 @@ DEVICE_BINARY_SENSORS: dict[str, tuple[OneWireBinarySensorEntityDescription, ...
|
||||
OneWireBinarySensorEntityDescription(
|
||||
key=f"sensed.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Sensed {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key=f"sensed_{id}",
|
||||
)
|
||||
for id in DEVICE_KEYS_0_7
|
||||
),
|
||||
@@ -55,8 +55,8 @@ DEVICE_BINARY_SENSORS: dict[str, tuple[OneWireBinarySensorEntityDescription, ...
|
||||
OneWireBinarySensorEntityDescription(
|
||||
key=f"sensed.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Sensed {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key=f"sensed_{id.lower()}",
|
||||
)
|
||||
for id in DEVICE_KEYS_A_B
|
||||
),
|
||||
@@ -69,10 +69,10 @@ HOBBYBOARD_EF: dict[str, tuple[OneWireBinarySensorEntityDescription, ...]] = {
|
||||
OneWireBinarySensorEntityDescription(
|
||||
key=f"hub/short.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Hub Short on Branch {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
translation_key=f"hub_short_{id}",
|
||||
)
|
||||
for id in DEVICE_KEYS_0_3
|
||||
),
|
||||
@@ -120,14 +120,12 @@ def get_entities(onewire_hub: OneWireHub) -> list[OneWireBinarySensor]:
|
||||
continue
|
||||
for description in get_sensor_types(device_sub_type)[family]:
|
||||
device_file = os.path.join(os.path.split(device.path)[0], description.key)
|
||||
name = f"{device_id} {description.name}"
|
||||
entities.append(
|
||||
OneWireBinarySensor(
|
||||
description=description,
|
||||
device_id=device_id,
|
||||
device_file=device_file,
|
||||
device_info=device_info,
|
||||
name=name,
|
||||
owproxy=onewire_hub.owproxy,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@ class OneWireEntity(Entity):
|
||||
"""Implementation of a 1-Wire entity."""
|
||||
|
||||
entity_description: OneWireEntityDescription
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -34,7 +35,6 @@ class OneWireEntity(Entity):
|
||||
device_id: str,
|
||||
device_info: DeviceInfo,
|
||||
device_file: str,
|
||||
name: str,
|
||||
owproxy: protocol._Proxy,
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
@@ -42,7 +42,6 @@ class OneWireEntity(Entity):
|
||||
self._last_update_success = True
|
||||
self._attr_unique_id = f"/{device_id}/{description.key}"
|
||||
self._attr_device_info = device_info
|
||||
self._attr_name = name
|
||||
self._device_file = device_file
|
||||
self._state: StateType = None
|
||||
self._value_raw: float | None = None
|
||||
|
||||
@@ -70,10 +70,10 @@ def _get_sensor_precision_family_28(device_id: str, options: Mapping[str, Any])
|
||||
SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION = OneWireSensorEntityDescription(
|
||||
key="temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="temperature",
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -86,19 +86,19 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
|
||||
key="TAI8570/temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="temperature",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="TAI8570/pressure",
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.MBAR,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="pressure",
|
||||
),
|
||||
),
|
||||
"22": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
|
||||
@@ -108,102 +108,102 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
|
||||
key="humidity",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="humidity",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="HIH3600/humidity",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Humidity HIH3600",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="humidity_hih3600",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="HIH4000/humidity",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Humidity HIH4000",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="humidity_hih4000",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="HIH5030/humidity",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Humidity HIH5030",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="humidity_hih5030",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="HTM1735/humidity",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Humidity HTM1735",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="humidity_htm1735",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="B1-R1-A/pressure",
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.MBAR,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="pressure",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="S3-R1-A/illuminance",
|
||||
device_class=SensorDeviceClass.ILLUMINANCE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Illuminance",
|
||||
native_unit_of_measurement=LIGHT_LUX,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="illuminance",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="VAD",
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Voltage VAD",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="voltage_vad",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="VDD",
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Voltage VDD",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="voltage_vdd",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="vis",
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="vis",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="voltage_vis",
|
||||
),
|
||||
),
|
||||
"28": (
|
||||
OneWireSensorEntityDescription(
|
||||
key="temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
override_key=_get_sensor_precision_family_28,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="temperature",
|
||||
),
|
||||
),
|
||||
"30": (
|
||||
@@ -212,29 +212,29 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
|
||||
key="typeX/temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Thermocouple temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
override_key=lambda d, o: "typeK/temperature",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="thermocouple_temperature_k",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="volt",
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="Voltage",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="voltage",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="vis",
|
||||
device_class=SensorDeviceClass.VOLTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
name="vis",
|
||||
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="voltage_vis_gradient",
|
||||
),
|
||||
),
|
||||
"3B": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
|
||||
@@ -242,10 +242,10 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
|
||||
"1D": tuple(
|
||||
OneWireSensorEntityDescription(
|
||||
key=f"counter.{id}",
|
||||
name=f"Counter {id}",
|
||||
native_unit_of_measurement="count",
|
||||
read_mode=READ_MODE_INT,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
translation_key=f"counter_{id.lower()}",
|
||||
)
|
||||
for id in DEVICE_KEYS_A_B
|
||||
),
|
||||
@@ -258,36 +258,36 @@ HOBBYBOARD_EF: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
|
||||
OneWireSensorEntityDescription(
|
||||
key="humidity/humidity_corrected",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
name="Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="humidity",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="humidity/humidity_raw",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
name="Humidity Raw",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="humidity_raw",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="humidity/temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="temperature",
|
||||
),
|
||||
),
|
||||
"HB_MOISTURE_METER": tuple(
|
||||
OneWireSensorEntityDescription(
|
||||
key=f"moisture/sensor.{id}",
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
name=f"Moisture {id}",
|
||||
native_unit_of_measurement=UnitOfPressure.CBAR,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key=f"moisture_{id}",
|
||||
)
|
||||
for id in DEVICE_KEYS_0_3
|
||||
),
|
||||
@@ -300,52 +300,52 @@ EDS_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
|
||||
OneWireSensorEntityDescription(
|
||||
key="EDS0066/temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="temperature",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="EDS0066/pressure",
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.MBAR,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="pressure",
|
||||
),
|
||||
),
|
||||
"EDS0068": (
|
||||
OneWireSensorEntityDescription(
|
||||
key="EDS0068/temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="temperature",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="EDS0068/pressure",
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.MBAR,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="pressure",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="EDS0068/light",
|
||||
device_class=SensorDeviceClass.ILLUMINANCE,
|
||||
name="Illuminance",
|
||||
native_unit_of_measurement=LIGHT_LUX,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="illuminance",
|
||||
),
|
||||
OneWireSensorEntityDescription(
|
||||
key="EDS0068/humidity",
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
name="Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
read_mode=READ_MODE_FLOAT,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
translation_key="humidity",
|
||||
),
|
||||
),
|
||||
}
|
||||
@@ -412,7 +412,8 @@ def get_entities(
|
||||
description = copy.deepcopy(description)
|
||||
description.device_class = SensorDeviceClass.HUMIDITY
|
||||
description.native_unit_of_measurement = PERCENTAGE
|
||||
description.name = f"Wetness {s_id}"
|
||||
description.translation_key = f"wetness_{s_id}"
|
||||
_LOGGER.info(description.translation_key)
|
||||
override_key = None
|
||||
if description.override_key:
|
||||
override_key = description.override_key(device_id, options)
|
||||
@@ -420,7 +421,6 @@ def get_entities(
|
||||
os.path.split(device.path)[0],
|
||||
override_key or description.key,
|
||||
)
|
||||
name = f"{device_id} {description.name}"
|
||||
if family == "12":
|
||||
# We need to check if there is TAI8570 plugged in
|
||||
try:
|
||||
@@ -438,7 +438,6 @@ def get_entities(
|
||||
device_id=device_id,
|
||||
device_file=device_file,
|
||||
device_info=device_info,
|
||||
name=name,
|
||||
owproxy=onewire_hub.owproxy,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -16,6 +16,233 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"sensed_a": {
|
||||
"name": "Sensed A"
|
||||
},
|
||||
"sensed_b": {
|
||||
"name": "Sensed B"
|
||||
},
|
||||
"sensed_0": {
|
||||
"name": "Sensed 0"
|
||||
},
|
||||
"sensed_1": {
|
||||
"name": "Sensed 1"
|
||||
},
|
||||
"sensed_2": {
|
||||
"name": "Sensed 2"
|
||||
},
|
||||
"sensed_3": {
|
||||
"name": "Sensed 3"
|
||||
},
|
||||
"sensed_4": {
|
||||
"name": "Sensed 4"
|
||||
},
|
||||
"sensed_5": {
|
||||
"name": "Sensed 5"
|
||||
},
|
||||
"sensed_6": {
|
||||
"name": "Sensed 6"
|
||||
},
|
||||
"sensed_7": {
|
||||
"name": "Sensed 7"
|
||||
},
|
||||
"hub_short_0": {
|
||||
"name": "Hub short on branch 0"
|
||||
},
|
||||
"hub_short_1": {
|
||||
"name": "Hub short on branch 1"
|
||||
},
|
||||
"hub_short_2": {
|
||||
"name": "Hub short on branch 2"
|
||||
},
|
||||
"hub_short_3": {
|
||||
"name": "Hub short on branch 3"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"counter_a": {
|
||||
"name": "Counter A"
|
||||
},
|
||||
"counter_b": {
|
||||
"name": "Counter B"
|
||||
},
|
||||
"humidity": {
|
||||
"name": "[%key:component::sensor::entity_component::humidity::name%]"
|
||||
},
|
||||
"humidity_hih3600": {
|
||||
"name": "HIH3600 humidity"
|
||||
},
|
||||
"humidity_hih4000": {
|
||||
"name": "HIH4000 humidity"
|
||||
},
|
||||
"humidity_hih5030": {
|
||||
"name": "HIH5030 humidity"
|
||||
},
|
||||
"humidity_htm1735": {
|
||||
"name": "HTM1735 humidity"
|
||||
},
|
||||
"humidity_raw": {
|
||||
"name": "Raw humidity"
|
||||
},
|
||||
"illuminance": {
|
||||
"name": "[%key:component::sensor::entity_component::illuminance::name%]"
|
||||
},
|
||||
"moisture_1": {
|
||||
"name": "Moisture 1"
|
||||
},
|
||||
"moisture_2": {
|
||||
"name": "Moisture 2"
|
||||
},
|
||||
"moisture_3": {
|
||||
"name": "Moisture 3"
|
||||
},
|
||||
"moisture_4": {
|
||||
"name": "Moisture 4"
|
||||
},
|
||||
"pressure": {
|
||||
"name": "[%key:component::sensor::entity_component::pressure::name%]"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"thermocouple_temperature_k": {
|
||||
"name": "Thermocouple K temperature"
|
||||
},
|
||||
"voltage": {
|
||||
"name": "[%key:component::sensor::entity_component::voltage::name%]"
|
||||
},
|
||||
"voltage_vad": {
|
||||
"name": "VAD voltage"
|
||||
},
|
||||
"voltage_vdd": {
|
||||
"name": "VDD voltage"
|
||||
},
|
||||
"voltage_vis": {
|
||||
"name": "VIS voltage difference"
|
||||
},
|
||||
"voltage_vis_gradient": {
|
||||
"name": "VIS voltage gradient"
|
||||
},
|
||||
"wetness_0": {
|
||||
"name": "Wetness 0"
|
||||
},
|
||||
"wetness_1": {
|
||||
"name": "Wetness 1"
|
||||
},
|
||||
"wetness_2": {
|
||||
"name": "Wetness 2"
|
||||
},
|
||||
"wetness_3": {
|
||||
"name": "Wetness 3"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"hub_branch_0": {
|
||||
"name": "Hub branch 0"
|
||||
},
|
||||
"hub_branch_1": {
|
||||
"name": "Hub branch 1"
|
||||
},
|
||||
"hub_branch_2": {
|
||||
"name": "Hub branch 2"
|
||||
},
|
||||
"hub_branch_3": {
|
||||
"name": "Hub branch 3"
|
||||
},
|
||||
"iad": {
|
||||
"name": "Current A/D control"
|
||||
},
|
||||
"latch_0": {
|
||||
"name": "Latch 0"
|
||||
},
|
||||
"latch_1": {
|
||||
"name": "Latch 1"
|
||||
},
|
||||
"latch_2": {
|
||||
"name": "Latch 2"
|
||||
},
|
||||
"latch_3": {
|
||||
"name": "Latch 3"
|
||||
},
|
||||
"latch_4": {
|
||||
"name": "Latch 4"
|
||||
},
|
||||
"latch_5": {
|
||||
"name": "Latch 5"
|
||||
},
|
||||
"latch_6": {
|
||||
"name": "Latch 6"
|
||||
},
|
||||
"latch_7": {
|
||||
"name": "Latch 7"
|
||||
},
|
||||
"latch_a": {
|
||||
"name": "Latch A"
|
||||
},
|
||||
"latch_b": {
|
||||
"name": "Latch B"
|
||||
},
|
||||
"leaf_sensor_0": {
|
||||
"name": "Leaf sensor 0"
|
||||
},
|
||||
"leaf_sensor_1": {
|
||||
"name": "Leaf sensor 1"
|
||||
},
|
||||
"leaf_sensor_2": {
|
||||
"name": "Leaf sensor 2"
|
||||
},
|
||||
"leaf_sensor_3": {
|
||||
"name": "Leaf sensor 3"
|
||||
},
|
||||
"moisture_sensor_0": {
|
||||
"name": "Moisture sensor 0"
|
||||
},
|
||||
"moisture_sensor_1": {
|
||||
"name": "Moisture sensor 1"
|
||||
},
|
||||
"moisture_sensor_2": {
|
||||
"name": "Moisture sensor 2"
|
||||
},
|
||||
"moisture_sensor_3": {
|
||||
"name": "Moisture sensor 3"
|
||||
},
|
||||
"pio": {
|
||||
"name": "Programmed input-output"
|
||||
},
|
||||
"pio_0": {
|
||||
"name": "Programmed input-output 0"
|
||||
},
|
||||
"pio_1": {
|
||||
"name": "Programmed input-output 1"
|
||||
},
|
||||
"pio_2": {
|
||||
"name": "Programmed input-output 2"
|
||||
},
|
||||
"pio_3": {
|
||||
"name": "Programmed input-output 3"
|
||||
},
|
||||
"pio_4": {
|
||||
"name": "Programmed input-output 4"
|
||||
},
|
||||
"pio_5": {
|
||||
"name": "Programmed input-output 5"
|
||||
},
|
||||
"pio_6": {
|
||||
"name": "Programmed input-output 6"
|
||||
},
|
||||
"pio_7": {
|
||||
"name": "Programmed input-output 7"
|
||||
},
|
||||
"pio_a": {
|
||||
"name": "Programmed input-output A"
|
||||
},
|
||||
"pio_b": {
|
||||
"name": "Programmed input-output B"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"device_not_selected": "Select devices to configure"
|
||||
|
||||
@@ -32,8 +32,8 @@ DEVICE_SWITCHES: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key="PIO",
|
||||
entity_registry_enabled_default=False,
|
||||
name="PIO",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key="pio",
|
||||
),
|
||||
),
|
||||
"12": tuple(
|
||||
@@ -41,8 +41,8 @@ DEVICE_SWITCHES: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key=f"PIO.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"PIO {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key=f"pio_{id.lower()}",
|
||||
)
|
||||
for id in DEVICE_KEYS_A_B
|
||||
]
|
||||
@@ -50,8 +50,8 @@ DEVICE_SWITCHES: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key=f"latch.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Latch {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key=f"latch_{id.lower()}",
|
||||
)
|
||||
for id in DEVICE_KEYS_A_B
|
||||
]
|
||||
@@ -61,8 +61,8 @@ DEVICE_SWITCHES: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
key="IAD",
|
||||
entity_registry_enabled_default=False,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
name="IAD",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key="iad",
|
||||
),
|
||||
),
|
||||
"29": tuple(
|
||||
@@ -70,8 +70,8 @@ DEVICE_SWITCHES: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key=f"PIO.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"PIO {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key=f"pio_{id}",
|
||||
)
|
||||
for id in DEVICE_KEYS_0_7
|
||||
]
|
||||
@@ -79,8 +79,8 @@ DEVICE_SWITCHES: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key=f"latch.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Latch {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key=f"latch_{id}",
|
||||
)
|
||||
for id in DEVICE_KEYS_0_7
|
||||
]
|
||||
@@ -89,8 +89,8 @@ DEVICE_SWITCHES: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key=f"PIO.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"PIO {id}",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
translation_key=f"pio_{id.lower()}",
|
||||
)
|
||||
for id in DEVICE_KEYS_A_B
|
||||
),
|
||||
@@ -104,9 +104,9 @@ HOBBYBOARD_EF: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key=f"hub/branch.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Hub Branch {id} Enable",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
translation_key=f"hub_branch_{id}",
|
||||
)
|
||||
for id in DEVICE_KEYS_0_3
|
||||
),
|
||||
@@ -115,9 +115,9 @@ HOBBYBOARD_EF: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key=f"moisture/is_leaf.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Leaf Sensor {id} Enable",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
translation_key=f"leaf_sensor_{id}",
|
||||
)
|
||||
for id in DEVICE_KEYS_0_3
|
||||
]
|
||||
@@ -125,9 +125,9 @@ HOBBYBOARD_EF: dict[str, tuple[OneWireEntityDescription, ...]] = {
|
||||
OneWireSwitchEntityDescription(
|
||||
key=f"moisture/is_moisture.{id}",
|
||||
entity_registry_enabled_default=False,
|
||||
name=f"Moisture Sensor {id} Enable",
|
||||
read_mode=READ_MODE_BOOL,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
translation_key=f"moisture_sensor_{id}",
|
||||
)
|
||||
for id in DEVICE_KEYS_0_3
|
||||
]
|
||||
@@ -177,14 +177,12 @@ def get_entities(onewire_hub: OneWireHub) -> list[OneWireSwitch]:
|
||||
continue
|
||||
for description in get_sensor_types(device_sub_type)[family]:
|
||||
device_file = os.path.join(os.path.split(device.path)[0], description.key)
|
||||
name = f"{device_id} {description.name}"
|
||||
entities.append(
|
||||
OneWireSwitch(
|
||||
description=description,
|
||||
device_id=device_id,
|
||||
device_file=device_file,
|
||||
device_info=device_info,
|
||||
name=name,
|
||||
owproxy=onewire_hub.owproxy,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2,91 +2,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable, Coroutine
|
||||
import dataclasses
|
||||
from functools import wraps
|
||||
from typing import Any, Concatenate, ParamSpec, TypeVar
|
||||
|
||||
import aiohttp
|
||||
import python_otbr_api
|
||||
from python_otbr_api import tlv_parser
|
||||
from python_otbr_api.pskc import compute_pskc
|
||||
|
||||
from homeassistant.components.thread import async_add_dataset
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import websocket_api
|
||||
from .const import DOMAIN
|
||||
|
||||
_R = TypeVar("_R")
|
||||
_P = ParamSpec("_P")
|
||||
|
||||
INSECURE_NETWORK_KEYS = (
|
||||
# Thread web UI default
|
||||
bytes.fromhex("00112233445566778899AABBCCDDEEFF"),
|
||||
)
|
||||
|
||||
INSECURE_PASSPHRASES = (
|
||||
# Thread web UI default
|
||||
"j01Nme",
|
||||
# Thread documentation default
|
||||
"J01NME",
|
||||
)
|
||||
|
||||
|
||||
def _handle_otbr_error(
|
||||
func: Callable[Concatenate[OTBRData, _P], Coroutine[Any, Any, _R]]
|
||||
) -> Callable[Concatenate[OTBRData, _P], Coroutine[Any, Any, _R]]:
|
||||
"""Handle OTBR errors."""
|
||||
|
||||
@wraps(func)
|
||||
async def _func(self: OTBRData, *args: _P.args, **kwargs: _P.kwargs) -> _R:
|
||||
try:
|
||||
return await func(self, *args, **kwargs)
|
||||
except python_otbr_api.OTBRError as exc:
|
||||
raise HomeAssistantError("Failed to call OTBR API") from exc
|
||||
|
||||
return _func
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class OTBRData:
|
||||
"""Container for OTBR data."""
|
||||
|
||||
url: str
|
||||
api: python_otbr_api.OTBR
|
||||
|
||||
@_handle_otbr_error
|
||||
async def set_enabled(self, enabled: bool) -> None:
|
||||
"""Enable or disable the router."""
|
||||
return await self.api.set_enabled(enabled)
|
||||
|
||||
@_handle_otbr_error
|
||||
async def get_active_dataset_tlvs(self) -> bytes | None:
|
||||
"""Get current active operational dataset in TLVS format, or None."""
|
||||
return await self.api.get_active_dataset_tlvs()
|
||||
|
||||
@_handle_otbr_error
|
||||
async def create_active_dataset(
|
||||
self, dataset: python_otbr_api.OperationalDataSet
|
||||
) -> None:
|
||||
"""Create an active operational dataset."""
|
||||
return await self.api.create_active_dataset(dataset)
|
||||
|
||||
@_handle_otbr_error
|
||||
async def set_active_dataset_tlvs(self, dataset: bytes) -> None:
|
||||
"""Set current active operational dataset in TLVS format."""
|
||||
await self.api.set_active_dataset_tlvs(dataset)
|
||||
|
||||
@_handle_otbr_error
|
||||
async def get_extended_address(self) -> bytes:
|
||||
"""Get extended address (EUI-64)."""
|
||||
return await self.api.get_extended_address()
|
||||
from .util import OTBRData, update_issues
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
@@ -95,54 +24,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def _warn_on_default_network_settings(
|
||||
hass: HomeAssistant, entry: ConfigEntry, dataset_tlvs: bytes
|
||||
) -> None:
|
||||
"""Warn user if insecure default network settings are used."""
|
||||
dataset = tlv_parser.parse_tlv(dataset_tlvs.hex())
|
||||
insecure = False
|
||||
|
||||
if (
|
||||
network_key := dataset.get(tlv_parser.MeshcopTLVType.NETWORKKEY)
|
||||
) is not None and bytes.fromhex(network_key) in INSECURE_NETWORK_KEYS:
|
||||
insecure = True
|
||||
if (
|
||||
not insecure
|
||||
and tlv_parser.MeshcopTLVType.EXTPANID in dataset
|
||||
and tlv_parser.MeshcopTLVType.NETWORKNAME in dataset
|
||||
and tlv_parser.MeshcopTLVType.PSKC in dataset
|
||||
):
|
||||
ext_pan_id = dataset[tlv_parser.MeshcopTLVType.EXTPANID]
|
||||
network_name = dataset[tlv_parser.MeshcopTLVType.NETWORKNAME]
|
||||
pskc = bytes.fromhex(dataset[tlv_parser.MeshcopTLVType.PSKC])
|
||||
for passphrase in INSECURE_PASSPHRASES:
|
||||
if pskc == compute_pskc(ext_pan_id, network_name, passphrase):
|
||||
insecure = True
|
||||
break
|
||||
|
||||
if insecure:
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"insecure_thread_network_{entry.entry_id}",
|
||||
is_fixable=False,
|
||||
is_persistent=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="insecure_thread_network",
|
||||
)
|
||||
else:
|
||||
ir.async_delete_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"insecure_thread_network_{entry.entry_id}",
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up an Open Thread Border Router config entry."""
|
||||
api = python_otbr_api.OTBR(entry.data["url"], async_get_clientsession(hass), 10)
|
||||
|
||||
otbrdata = OTBRData(entry.data["url"], api)
|
||||
otbrdata = OTBRData(entry.data["url"], api, entry.entry_id)
|
||||
try:
|
||||
dataset_tlvs = await otbrdata.get_active_dataset_tlvs()
|
||||
except (
|
||||
@@ -152,7 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
) as err:
|
||||
raise ConfigEntryNotReady("Unable to connect") from err
|
||||
if dataset_tlvs:
|
||||
_warn_on_default_network_settings(hass, entry, dataset_tlvs)
|
||||
await update_issues(hass, otbrdata, dataset_tlvs)
|
||||
await async_add_dataset(hass, DOMAIN, dataset_tlvs.hex())
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "otbr",
|
||||
"name": "Open Thread Border Router",
|
||||
"after_dependencies": ["hassio", "zha"],
|
||||
"after_dependencies": ["hassio", "homeassistant_yellow", "zha"],
|
||||
"codeowners": ["@home-assistant/core"],
|
||||
"config_flow": true,
|
||||
"dependencies": ["homeassistant_hardware", "thread"],
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
"insecure_thread_network": {
|
||||
"title": "Insecure Thread network settings detected",
|
||||
"description": "Your Thread network is using a default network key or pass phrase.\n\nThis is a security risk, please create a new Thread network."
|
||||
},
|
||||
"otbr_zha_channel_collision": {
|
||||
"title": "OTBR and ZHA share the same radio but use different channels",
|
||||
"description": "When OTBR and ZHA share the radio, they must use the same network channel.\n\nIf OTBR and ZHA attempt to connect to networks on different channels, neither Thread/Matter nor Zigbee will work.\n\nOTBR is configured with a Thread network on channel {otbr_channel}, ZHA is configured with a Zigbee network on channel {zha_channel}."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,98 @@
|
||||
"""Utility functions for the Open Thread Border Router integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
import contextlib
|
||||
import dataclasses
|
||||
from functools import wraps
|
||||
from typing import Any, Concatenate, ParamSpec, TypeVar
|
||||
|
||||
import python_otbr_api
|
||||
from python_otbr_api import tlv_parser
|
||||
from python_otbr_api.pskc import compute_pskc
|
||||
|
||||
from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import (
|
||||
is_multiprotocol_url,
|
||||
multi_pan_addon_using_device,
|
||||
)
|
||||
from homeassistant.components.homeassistant_yellow import RADIO_DEVICE as YELLOW_RADIO
|
||||
from homeassistant.components.zha import api as zha_api
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_R = TypeVar("_R")
|
||||
_P = ParamSpec("_P")
|
||||
|
||||
INFO_URL_SKY_CONNECT = (
|
||||
"https://skyconnect.home-assistant.io/multiprotocol-channel-missmatch"
|
||||
)
|
||||
INFO_URL_YELLOW = "https://yellow.home-assistant.io/multiprotocol-channel-missmatch"
|
||||
|
||||
INSECURE_NETWORK_KEYS = (
|
||||
# Thread web UI default
|
||||
bytes.fromhex("00112233445566778899AABBCCDDEEFF"),
|
||||
)
|
||||
|
||||
INSECURE_PASSPHRASES = (
|
||||
# Thread web UI default
|
||||
"j01Nme",
|
||||
# Thread documentation default
|
||||
"J01NME",
|
||||
)
|
||||
|
||||
|
||||
def _handle_otbr_error(
|
||||
func: Callable[Concatenate[OTBRData, _P], Coroutine[Any, Any, _R]]
|
||||
) -> Callable[Concatenate[OTBRData, _P], Coroutine[Any, Any, _R]]:
|
||||
"""Handle OTBR errors."""
|
||||
|
||||
@wraps(func)
|
||||
async def _func(self: OTBRData, *args: _P.args, **kwargs: _P.kwargs) -> _R:
|
||||
try:
|
||||
return await func(self, *args, **kwargs)
|
||||
except python_otbr_api.OTBRError as exc:
|
||||
raise HomeAssistantError("Failed to call OTBR API") from exc
|
||||
|
||||
return _func
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class OTBRData:
|
||||
"""Container for OTBR data."""
|
||||
|
||||
url: str
|
||||
api: python_otbr_api.OTBR
|
||||
entry_id: str
|
||||
|
||||
@_handle_otbr_error
|
||||
async def set_enabled(self, enabled: bool) -> None:
|
||||
"""Enable or disable the router."""
|
||||
return await self.api.set_enabled(enabled)
|
||||
|
||||
@_handle_otbr_error
|
||||
async def get_active_dataset_tlvs(self) -> bytes | None:
|
||||
"""Get current active operational dataset in TLVS format, or None."""
|
||||
return await self.api.get_active_dataset_tlvs()
|
||||
|
||||
@_handle_otbr_error
|
||||
async def create_active_dataset(
|
||||
self, dataset: python_otbr_api.OperationalDataSet
|
||||
) -> None:
|
||||
"""Create an active operational dataset."""
|
||||
return await self.api.create_active_dataset(dataset)
|
||||
|
||||
@_handle_otbr_error
|
||||
async def set_active_dataset_tlvs(self, dataset: bytes) -> None:
|
||||
"""Set current active operational dataset in TLVS format."""
|
||||
await self.api.set_active_dataset_tlvs(dataset)
|
||||
|
||||
@_handle_otbr_error
|
||||
async def get_extended_address(self) -> bytes:
|
||||
"""Get extended address (EUI-64)."""
|
||||
return await self.api.get_extended_address()
|
||||
|
||||
|
||||
def _get_zha_url(hass: HomeAssistant) -> str | None:
|
||||
@@ -41,3 +126,104 @@ async def get_allowed_channel(hass: HomeAssistant, otbr_url: str) -> int | None:
|
||||
return None
|
||||
|
||||
return await _get_zha_channel(hass)
|
||||
|
||||
|
||||
async def _warn_on_channel_collision(
|
||||
hass: HomeAssistant, otbrdata: OTBRData, dataset_tlvs: bytes
|
||||
) -> None:
|
||||
"""Warn user if OTBR and ZHA attempt to use different channels."""
|
||||
|
||||
def delete_issue() -> None:
|
||||
ir.async_delete_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"otbr_zha_channel_collision_{otbrdata.entry_id}",
|
||||
)
|
||||
|
||||
if (allowed_channel := await get_allowed_channel(hass, otbrdata.url)) is None:
|
||||
delete_issue()
|
||||
return
|
||||
|
||||
dataset = tlv_parser.parse_tlv(dataset_tlvs.hex())
|
||||
|
||||
if (channel_s := dataset.get(tlv_parser.MeshcopTLVType.CHANNEL)) is None:
|
||||
delete_issue()
|
||||
return
|
||||
try:
|
||||
channel = int(channel_s, 16)
|
||||
except ValueError:
|
||||
delete_issue()
|
||||
return
|
||||
|
||||
if channel == allowed_channel:
|
||||
delete_issue()
|
||||
return
|
||||
|
||||
yellow = await multi_pan_addon_using_device(hass, YELLOW_RADIO)
|
||||
learn_more_url = INFO_URL_YELLOW if yellow else INFO_URL_SKY_CONNECT
|
||||
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"otbr_zha_channel_collision_{otbrdata.entry_id}",
|
||||
is_fixable=False,
|
||||
is_persistent=False,
|
||||
learn_more_url=learn_more_url,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="otbr_zha_channel_collision",
|
||||
translation_placeholders={
|
||||
"otbr_channel": str(channel),
|
||||
"zha_channel": str(allowed_channel),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _warn_on_default_network_settings(
|
||||
hass: HomeAssistant, otbrdata: OTBRData, dataset_tlvs: bytes
|
||||
) -> None:
|
||||
"""Warn user if insecure default network settings are used."""
|
||||
dataset = tlv_parser.parse_tlv(dataset_tlvs.hex())
|
||||
insecure = False
|
||||
|
||||
if (
|
||||
network_key := dataset.get(tlv_parser.MeshcopTLVType.NETWORKKEY)
|
||||
) is not None and bytes.fromhex(network_key) in INSECURE_NETWORK_KEYS:
|
||||
insecure = True
|
||||
if (
|
||||
not insecure
|
||||
and tlv_parser.MeshcopTLVType.EXTPANID in dataset
|
||||
and tlv_parser.MeshcopTLVType.NETWORKNAME in dataset
|
||||
and tlv_parser.MeshcopTLVType.PSKC in dataset
|
||||
):
|
||||
ext_pan_id = dataset[tlv_parser.MeshcopTLVType.EXTPANID]
|
||||
network_name = dataset[tlv_parser.MeshcopTLVType.NETWORKNAME]
|
||||
pskc = bytes.fromhex(dataset[tlv_parser.MeshcopTLVType.PSKC])
|
||||
for passphrase in INSECURE_PASSPHRASES:
|
||||
if pskc == compute_pskc(ext_pan_id, network_name, passphrase):
|
||||
insecure = True
|
||||
break
|
||||
|
||||
if insecure:
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"insecure_thread_network_{otbrdata.entry_id}",
|
||||
is_fixable=False,
|
||||
is_persistent=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="insecure_thread_network",
|
||||
)
|
||||
else:
|
||||
ir.async_delete_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"insecure_thread_network_{otbrdata.entry_id}",
|
||||
)
|
||||
|
||||
|
||||
async def update_issues(
|
||||
hass: HomeAssistant, otbrdata: OTBRData, dataset_tlvs: bytes
|
||||
) -> None:
|
||||
"""Raise or clear repair issues related to network settings."""
|
||||
await _warn_on_channel_collision(hass, otbrdata, dataset_tlvs)
|
||||
_warn_on_default_network_settings(hass, otbrdata, dataset_tlvs)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Websocket API for OTBR."""
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import python_otbr_api
|
||||
from python_otbr_api import tlv_parser
|
||||
@@ -11,10 +10,7 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .const import DEFAULT_CHANNEL, DOMAIN
|
||||
from .util import get_allowed_channel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import OTBRData
|
||||
from .util import OTBRData, get_allowed_channel, update_issues
|
||||
|
||||
|
||||
@callback
|
||||
@@ -109,6 +105,9 @@ async def websocket_create_network(
|
||||
|
||||
await async_add_dataset(hass, DOMAIN, dataset_tlvs.hex())
|
||||
|
||||
# Update repair issues
|
||||
await update_issues(hass, data, dataset_tlvs)
|
||||
|
||||
connection.send_result(msg["id"])
|
||||
|
||||
|
||||
@@ -167,6 +166,9 @@ async def websocket_set_network(
|
||||
connection.send_error(msg["id"], "set_enabled_failed", str(exc))
|
||||
return
|
||||
|
||||
# Update repair issues
|
||||
await update_issues(hass, data, bytes.fromhex(dataset_tlv))
|
||||
|
||||
connection.send_result(msg["id"])
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ BINARY_SENSOR_TYPES: tuple[PiHoleBinarySensorEntityDescription, ...] = (
|
||||
),
|
||||
PiHoleBinarySensorEntityDescription(
|
||||
key="status",
|
||||
name="Status",
|
||||
translation_key="status",
|
||||
icon="mdi:pi-hole",
|
||||
state_value=lambda api: bool(api.data.get("status") == "enabled"),
|
||||
),
|
||||
@@ -109,6 +109,7 @@ class PiHoleBinarySensor(PiHoleEntity, BinarySensorEntity):
|
||||
"""Representation of a Pi-hole binary sensor."""
|
||||
|
||||
entity_description: PiHoleBinarySensorEntityDescription
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -121,12 +122,7 @@ class PiHoleBinarySensor(PiHoleEntity, BinarySensorEntity):
|
||||
"""Initialize a Pi-hole sensor."""
|
||||
super().__init__(api, coordinator, name, server_unique_id)
|
||||
self.entity_description = description
|
||||
|
||||
if description.key == "status":
|
||||
self._attr_name = f"{name}"
|
||||
else:
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
self._attr_unique_id = f"{self._server_unique_id}/{description.name}"
|
||||
self._attr_unique_id = f"{self._server_unique_id}/{description.key}"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
|
||||
@@ -17,55 +17,55 @@ from .const import DATA_KEY_API, DATA_KEY_COORDINATOR, DOMAIN as PIHOLE_DOMAIN
|
||||
SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key="ads_blocked_today",
|
||||
name="Ads Blocked Today",
|
||||
translation_key="ads_blocked_today",
|
||||
native_unit_of_measurement="ads",
|
||||
icon="mdi:close-octagon-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="ads_percentage_today",
|
||||
name="Ads Percentage Blocked Today",
|
||||
translation_key="ads_percentage_today",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
icon="mdi:close-octagon-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="clients_ever_seen",
|
||||
name="Seen Clients",
|
||||
translation_key="clients_ever_seen",
|
||||
native_unit_of_measurement="clients",
|
||||
icon="mdi:account-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="dns_queries_today",
|
||||
name="DNS Queries Today",
|
||||
translation_key="dns_queries_today",
|
||||
native_unit_of_measurement="queries",
|
||||
icon="mdi:comment-question-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="domains_being_blocked",
|
||||
name="Domains Blocked",
|
||||
translation_key="domains_being_blocked",
|
||||
native_unit_of_measurement="domains",
|
||||
icon="mdi:block-helper",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="queries_cached",
|
||||
name="DNS Queries Cached",
|
||||
translation_key="queries_cached",
|
||||
native_unit_of_measurement="queries",
|
||||
icon="mdi:comment-question-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="queries_forwarded",
|
||||
name="DNS Queries Forwarded",
|
||||
translation_key="queries_forwarded",
|
||||
native_unit_of_measurement="queries",
|
||||
icon="mdi:comment-question-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="unique_clients",
|
||||
name="DNS Unique Clients",
|
||||
translation_key="unique_clients",
|
||||
native_unit_of_measurement="clients",
|
||||
icon="mdi:account-outline",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="unique_domains",
|
||||
name="DNS Unique Domains",
|
||||
translation_key="unique_domains",
|
||||
native_unit_of_measurement="domains",
|
||||
icon="mdi:domain",
|
||||
),
|
||||
@@ -95,6 +95,7 @@ class PiHoleSensor(PiHoleEntity, SensorEntity):
|
||||
"""Representation of a Pi-hole sensor."""
|
||||
|
||||
entity_description: SensorEntityDescription
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -108,8 +109,7 @@ class PiHoleSensor(PiHoleEntity, SensorEntity):
|
||||
super().__init__(api, coordinator, name, server_unique_id)
|
||||
self.entity_description = description
|
||||
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
self._attr_unique_id = f"{self._server_unique_id}/{description.name}"
|
||||
self._attr_unique_id = f"{self._server_unique_id}/{description.key}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
|
||||
@@ -32,5 +32,26 @@
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"status": { "name": "Status" }
|
||||
},
|
||||
"sensor": {
|
||||
"ads_blocked_today": { "name": "Ads blocked today" },
|
||||
"ads_percentage_today": { "name": "Ads percentage blocked today" },
|
||||
"clients_ever_seen": { "name": "Seen clients" },
|
||||
"dns_queries_today": { "name": "DNS queries today" },
|
||||
"domains_being_blocked": { "name": "Domains blocked" },
|
||||
"queries_cached": { "name": "DNS queries cached" },
|
||||
"queries_forwarded": { "name": "DNS queries forwarded" },
|
||||
"unique_clients": { "name": "DNS unique clients" },
|
||||
"unique_domains": { "name": "DNS unique domains" }
|
||||
},
|
||||
"update": {
|
||||
"core_update_available": { "name": "Core update available" },
|
||||
"ftl_update_available": { "name": "FTL update available" },
|
||||
"web_update_available": { "name": "Web update available" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class PiHoleUpdateEntityDescription(UpdateEntityDescription):
|
||||
UPDATE_ENTITY_TYPES: tuple[PiHoleUpdateEntityDescription, ...] = (
|
||||
PiHoleUpdateEntityDescription(
|
||||
key="core_update_available",
|
||||
name="Core Update Available",
|
||||
translation_key="core_update_available",
|
||||
title="Pi-hole Core",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
installed_version=lambda versions: versions.get("core_current"),
|
||||
@@ -39,7 +39,7 @@ UPDATE_ENTITY_TYPES: tuple[PiHoleUpdateEntityDescription, ...] = (
|
||||
),
|
||||
PiHoleUpdateEntityDescription(
|
||||
key="web_update_available",
|
||||
name="Web Update Available",
|
||||
translation_key="web_update_available",
|
||||
title="Pi-hole Web interface",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
installed_version=lambda versions: versions.get("web_current"),
|
||||
@@ -48,7 +48,7 @@ UPDATE_ENTITY_TYPES: tuple[PiHoleUpdateEntityDescription, ...] = (
|
||||
),
|
||||
PiHoleUpdateEntityDescription(
|
||||
key="ftl_update_available",
|
||||
name="FTL Update Available",
|
||||
translation_key="ftl_update_available",
|
||||
title="Pi-hole FTL DNS",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
installed_version=lambda versions: versions.get("FTL_current"),
|
||||
@@ -81,6 +81,7 @@ class PiHoleUpdateEntity(PiHoleEntity, UpdateEntity):
|
||||
"""Representation of a Pi-hole update entity."""
|
||||
|
||||
entity_description: PiHoleUpdateEntityDescription
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -94,8 +95,7 @@ class PiHoleUpdateEntity(PiHoleEntity, UpdateEntity):
|
||||
super().__init__(api, coordinator, name, server_unique_id)
|
||||
self.entity_description = description
|
||||
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
self._attr_unique_id = f"{self._server_unique_id}/{description.name}"
|
||||
self._attr_unique_id = f"{self._server_unique_id}/{description.key}"
|
||||
self._attr_title = description.title
|
||||
|
||||
@property
|
||||
|
||||
@@ -31,26 +31,27 @@ class PlugwiseBinarySensorEntityDescription(BinarySensorEntityDescription):
|
||||
BINARY_SENSORS: tuple[PlugwiseBinarySensorEntityDescription, ...] = (
|
||||
PlugwiseBinarySensorEntityDescription(
|
||||
key="compressor_state",
|
||||
name="Compressor state",
|
||||
translation_key="compressor_state",
|
||||
icon="mdi:hvac",
|
||||
icon_off="mdi:hvac-off",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
PlugwiseBinarySensorEntityDescription(
|
||||
key="cooling_enabled",
|
||||
name="Cooling enabled",
|
||||
translation_key="cooling_enabled",
|
||||
icon="mdi:snowflake-thermometer",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
PlugwiseBinarySensorEntityDescription(
|
||||
key="dhw_state",
|
||||
name="DHW state",
|
||||
translation_key="dhw_state",
|
||||
icon="mdi:water-pump",
|
||||
icon_off="mdi:water-pump-off",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
PlugwiseBinarySensorEntityDescription(
|
||||
key="flame_state",
|
||||
translation_key="flame_state",
|
||||
name="Flame state",
|
||||
icon="mdi:fire",
|
||||
icon_off="mdi:fire-off",
|
||||
@@ -58,28 +59,28 @@ BINARY_SENSORS: tuple[PlugwiseBinarySensorEntityDescription, ...] = (
|
||||
),
|
||||
PlugwiseBinarySensorEntityDescription(
|
||||
key="heating_state",
|
||||
name="Heating",
|
||||
translation_key="heating_state",
|
||||
icon="mdi:radiator",
|
||||
icon_off="mdi:radiator-off",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
PlugwiseBinarySensorEntityDescription(
|
||||
key="cooling_state",
|
||||
name="Cooling",
|
||||
translation_key="cooling_state",
|
||||
icon="mdi:snowflake",
|
||||
icon_off="mdi:snowflake-off",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
PlugwiseBinarySensorEntityDescription(
|
||||
key="slave_boiler_state",
|
||||
name="Secondary boiler state",
|
||||
translation_key="slave_boiler_state",
|
||||
icon="mdi:fire",
|
||||
icon_off="mdi:circle-off-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
PlugwiseBinarySensorEntityDescription(
|
||||
key="plugwise_notification",
|
||||
name="Plugwise notification",
|
||||
translation_key="plugwise_notification",
|
||||
icon="mdi:mailbox-up-outline",
|
||||
icon_off="mdi:mailbox-outline",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
|
||||
@@ -43,9 +43,9 @@ class PlugwiseNumberEntityDescription(
|
||||
NUMBER_TYPES = (
|
||||
PlugwiseNumberEntityDescription(
|
||||
key="maximum_boiler_temperature",
|
||||
translation_key="maximum_boiler_temperature",
|
||||
command=lambda api, number, value: api.set_number_setpoint(number, value),
|
||||
device_class=NumberDeviceClass.TEMPERATURE,
|
||||
name="Maximum boiler temperature setpoint",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
native_max_value_key="upper_bound",
|
||||
native_min_value_key="lower_bound",
|
||||
|
||||
@@ -37,7 +37,7 @@ class PlugwiseSelectEntityDescription(
|
||||
SELECT_TYPES = (
|
||||
PlugwiseSelectEntityDescription(
|
||||
key="select_schedule",
|
||||
name="Thermostat schedule",
|
||||
translation_key="select_schedule",
|
||||
icon="mdi:calendar-clock",
|
||||
command=lambda api, loc, opt: api.set_schedule_state(loc, opt, STATE_ON),
|
||||
current_option_key="selected_schedule",
|
||||
@@ -45,20 +45,18 @@ SELECT_TYPES = (
|
||||
),
|
||||
PlugwiseSelectEntityDescription(
|
||||
key="select_regulation_mode",
|
||||
name="Regulation mode",
|
||||
translation_key="regulation_mode",
|
||||
icon="mdi:hvac",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
translation_key="regulation_mode",
|
||||
command=lambda api, loc, opt: api.set_regulation_mode(opt),
|
||||
current_option_key="regulation_mode",
|
||||
options_key="regulation_modes",
|
||||
),
|
||||
PlugwiseSelectEntityDescription(
|
||||
key="select_dhw_mode",
|
||||
name="DHW mode",
|
||||
translation_key="dhw_mode",
|
||||
icon="mdi:shower",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
translation_key="dhw_mode",
|
||||
command=lambda api, loc, opt: api.set_dhw_mode(opt),
|
||||
current_option_key="dhw_mode",
|
||||
options_key="dhw_modes",
|
||||
|
||||
@@ -26,38 +26,94 @@
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"compressor_state": {
|
||||
"name": "Compressor state"
|
||||
},
|
||||
"cooling_enabled": {
|
||||
"name": "Cooling enabled"
|
||||
},
|
||||
"dhw_state": {
|
||||
"name": "DHW state"
|
||||
},
|
||||
"flame_state": {
|
||||
"name": "Flame state"
|
||||
},
|
||||
"heating_state": {
|
||||
"name": "[%key:component::climate::entity_component::_::state_attributes::hvac_action::state::heating%]"
|
||||
},
|
||||
"cooling_state": {
|
||||
"name": "[%key:component::climate::entity_component::_::state_attributes::hvac_action::state::cooling%]"
|
||||
},
|
||||
"slave_boiler_state": {
|
||||
"name": "Secondary boiler state"
|
||||
},
|
||||
"plugwise_notification": {
|
||||
"name": "Plugwise notification"
|
||||
}
|
||||
},
|
||||
"climate": {
|
||||
"plugwise": {
|
||||
"state_attributes": {
|
||||
"available_schemas": {
|
||||
"name": "Available schemas"
|
||||
},
|
||||
"preset_mode": {
|
||||
"state": {
|
||||
"asleep": "Night",
|
||||
"away": "Away",
|
||||
"home": "Home",
|
||||
"away": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::away%]",
|
||||
"home": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::home%]",
|
||||
"no_frost": "Anti-frost",
|
||||
"vacation": "Vacation"
|
||||
}
|
||||
},
|
||||
"selected_schema": {
|
||||
"name": "Selected schema"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"maximum_boiler_temperature": {
|
||||
"name": "Maximum boiler temperature setpoint"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"dhw_mode": {
|
||||
"name": "DHW mode",
|
||||
"state": {
|
||||
"off": "Off",
|
||||
"off": "[%key:common::state::off%]",
|
||||
"auto": "Auto",
|
||||
"boost": "Boost",
|
||||
"comfort": "Comfort"
|
||||
"boost": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::boost%]",
|
||||
"comfort": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::comfort%]"
|
||||
}
|
||||
},
|
||||
"regulation_mode": {
|
||||
"name": "Regulation mode",
|
||||
"state": {
|
||||
"bleeding_cold": "Bleeding cold",
|
||||
"bleeding_hot": "Bleeding hot",
|
||||
"cooling": "Cooling",
|
||||
"heating": "Heating",
|
||||
"off": "Off"
|
||||
"cooling": "[%key:component::climate::entity_component::_::state_attributes::hvac_action::state::cooling%]",
|
||||
"heating": "[%key:component::climate::entity_component::_::state_attributes::hvac_action::state::heating%]",
|
||||
"off": "[%key:common::state::off%]"
|
||||
}
|
||||
},
|
||||
"select_schedule": {
|
||||
"name": "Thermostat schedule"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"cooling_ena_switch": {
|
||||
"name": "[%key:component::climate::entity_component::_::state_attributes::hvac_action::state::cooling%]"
|
||||
},
|
||||
"dhw_cm_switch": {
|
||||
"name": "DHW comfort mode"
|
||||
},
|
||||
"lock": {
|
||||
"name": "[%key:component::lock::entity_component::_::name%]"
|
||||
},
|
||||
"relay": {
|
||||
"name": "Relay"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,19 +21,19 @@ from .util import plugwise_command
|
||||
SWITCHES: tuple[SwitchEntityDescription, ...] = (
|
||||
SwitchEntityDescription(
|
||||
key="dhw_cm_switch",
|
||||
name="DHW comfort mode",
|
||||
translation_key="dhw_cm_switch",
|
||||
icon="mdi:water-plus",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
),
|
||||
SwitchEntityDescription(
|
||||
key="lock",
|
||||
name="Lock",
|
||||
translation_key="lock",
|
||||
icon="mdi:lock",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
),
|
||||
SwitchEntityDescription(
|
||||
key="relay",
|
||||
name="Relay",
|
||||
translation_key="relay",
|
||||
device_class=SwitchDeviceClass.SWITCH,
|
||||
),
|
||||
SwitchEntityDescription(
|
||||
|
||||
@@ -17,7 +17,7 @@ import voluptuous as vol
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_SCAN_INTERVAL, CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
@@ -29,6 +29,8 @@ SERVICE_START = "start"
|
||||
SERVICE_MEMORY = "memory"
|
||||
SERVICE_START_LOG_OBJECTS = "start_log_objects"
|
||||
SERVICE_STOP_LOG_OBJECTS = "stop_log_objects"
|
||||
SERVICE_START_LOG_OBJECT_SOURCES = "start_log_object_sources"
|
||||
SERVICE_STOP_LOG_OBJECT_SOURCES = "stop_log_object_sources"
|
||||
SERVICE_DUMP_LOG_OBJECTS = "dump_log_objects"
|
||||
SERVICE_LRU_STATS = "lru_stats"
|
||||
SERVICE_LOG_THREAD_FRAMES = "log_thread_frames"
|
||||
@@ -60,7 +62,10 @@ SERVICES = (
|
||||
|
||||
DEFAULT_SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
DEFAULT_MAX_OBJECTS = 5
|
||||
|
||||
CONF_SECONDS = "seconds"
|
||||
CONF_MAX_OBJECTS = "max_objects"
|
||||
|
||||
LOG_INTERVAL_SUB = "log_interval_subscription"
|
||||
|
||||
@@ -85,7 +90,7 @@ async def async_setup_entry( # noqa: C901
|
||||
|
||||
async def _async_start_log_objects(call: ServiceCall) -> None:
|
||||
if LOG_INTERVAL_SUB in domain_data:
|
||||
domain_data[LOG_INTERVAL_SUB]()
|
||||
raise HomeAssistantError("Object logging already started")
|
||||
|
||||
persistent_notification.async_create(
|
||||
hass,
|
||||
@@ -103,21 +108,53 @@ async def async_setup_entry( # noqa: C901
|
||||
|
||||
async def _async_stop_log_objects(call: ServiceCall) -> None:
|
||||
if LOG_INTERVAL_SUB not in domain_data:
|
||||
return
|
||||
raise HomeAssistantError("Object logging not running")
|
||||
|
||||
persistent_notification.async_dismiss(hass, "profile_object_logging")
|
||||
domain_data.pop(LOG_INTERVAL_SUB)()
|
||||
|
||||
def _safe_repr(obj: Any) -> str:
|
||||
"""Get the repr of an object but keep going if there is an exception.
|
||||
async def _async_start_object_sources(call: ServiceCall) -> None:
|
||||
if LOG_INTERVAL_SUB in domain_data:
|
||||
raise HomeAssistantError("Object logging already started")
|
||||
|
||||
We wrap repr to ensure if one object cannot be serialized, we can
|
||||
still get the rest.
|
||||
"""
|
||||
try:
|
||||
return repr(obj)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return f"Failed to serialize {type(obj)}"
|
||||
persistent_notification.async_create(
|
||||
hass,
|
||||
(
|
||||
"Object source logging has started. See [the logs](/config/logs) to"
|
||||
" track the growth of new objects."
|
||||
),
|
||||
title="Object source logging started",
|
||||
notification_id="profile_object_source_logging",
|
||||
)
|
||||
|
||||
last_ids: set[int] = set()
|
||||
last_stats: dict[str, int] = {}
|
||||
|
||||
async def _log_object_sources_with_max(*_: Any) -> None:
|
||||
await hass.async_add_executor_job(
|
||||
_log_object_sources, call.data[CONF_MAX_OBJECTS], last_ids, last_stats
|
||||
)
|
||||
|
||||
await _log_object_sources_with_max()
|
||||
cancel_track = async_track_time_interval(
|
||||
hass, _log_object_sources_with_max, call.data[CONF_SCAN_INTERVAL]
|
||||
)
|
||||
|
||||
@callback
|
||||
def _cancel():
|
||||
cancel_track()
|
||||
last_ids.clear()
|
||||
last_stats.clear()
|
||||
|
||||
domain_data[LOG_INTERVAL_SUB] = _cancel
|
||||
|
||||
@callback
|
||||
def _async_stop_object_sources(call: ServiceCall) -> None:
|
||||
if LOG_INTERVAL_SUB not in domain_data:
|
||||
raise HomeAssistantError("Object logging not running")
|
||||
|
||||
persistent_notification.async_dismiss(hass, "profile_object_source_logging")
|
||||
domain_data.pop(LOG_INTERVAL_SUB)()
|
||||
|
||||
def _dump_log_objects(call: ServiceCall) -> None:
|
||||
# Imports deferred to avoid loading modules
|
||||
@@ -143,15 +180,6 @@ async def async_setup_entry( # noqa: C901
|
||||
notification_id="profile_object_dump",
|
||||
)
|
||||
|
||||
def _get_function_absfile(func: Any) -> str:
|
||||
"""Get the absolute file path of a function."""
|
||||
import inspect # pylint: disable=import-outside-toplevel
|
||||
|
||||
abs_file = "unknown"
|
||||
with suppress(Exception):
|
||||
abs_file = inspect.getabsfile(func)
|
||||
return abs_file
|
||||
|
||||
def _lru_stats(call: ServiceCall) -> None:
|
||||
"""Log the stats of all lru caches."""
|
||||
# Imports deferred to avoid loading modules
|
||||
@@ -164,7 +192,7 @@ async def async_setup_entry( # noqa: C901
|
||||
_LOGGER.critical(
|
||||
"Cache stats for lru_cache %s at %s: %s",
|
||||
lru.__wrapped__,
|
||||
_get_function_absfile(lru.__wrapped__),
|
||||
_get_function_absfile(lru.__wrapped__) or "unknown",
|
||||
lru.cache_info(),
|
||||
)
|
||||
|
||||
@@ -175,7 +203,7 @@ async def async_setup_entry( # noqa: C901
|
||||
_LOGGER.critical(
|
||||
"Cache stats for LRU %s at %s: %s",
|
||||
type(class_with_lru_attr),
|
||||
_get_function_absfile(class_with_lru_attr),
|
||||
_get_function_absfile(class_with_lru_attr) or "unknown",
|
||||
maybe_lru.get_stats(),
|
||||
)
|
||||
|
||||
@@ -267,6 +295,30 @@ async def async_setup_entry( # noqa: C901
|
||||
_async_stop_log_objects,
|
||||
)
|
||||
|
||||
async_register_admin_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_START_LOG_OBJECT_SOURCES,
|
||||
_async_start_object_sources,
|
||||
schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
|
||||
): cv.time_period,
|
||||
vol.Optional(CONF_MAX_OBJECTS, default=DEFAULT_MAX_OBJECTS): vol.Range(
|
||||
min=1, max=1024
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
async_register_admin_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
SERVICE_STOP_LOG_OBJECT_SOURCES,
|
||||
_async_stop_object_sources,
|
||||
)
|
||||
|
||||
async_register_admin_service(
|
||||
hass,
|
||||
DOMAIN,
|
||||
@@ -404,3 +456,101 @@ def _log_objects(*_):
|
||||
import objgraph # pylint: disable=import-outside-toplevel
|
||||
|
||||
_LOGGER.critical("Memory Growth: %s", objgraph.growth(limit=1000))
|
||||
|
||||
|
||||
def _get_function_absfile(func: Any) -> str | None:
|
||||
"""Get the absolute file path of a function."""
|
||||
import inspect # pylint: disable=import-outside-toplevel
|
||||
|
||||
abs_file: str | None = None
|
||||
with suppress(Exception):
|
||||
abs_file = inspect.getabsfile(func)
|
||||
return abs_file
|
||||
|
||||
|
||||
def _safe_repr(obj: Any) -> str:
|
||||
"""Get the repr of an object but keep going if there is an exception.
|
||||
|
||||
We wrap repr to ensure if one object cannot be serialized, we can
|
||||
still get the rest.
|
||||
"""
|
||||
try:
|
||||
return repr(obj)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return f"Failed to serialize {type(obj)}"
|
||||
|
||||
|
||||
def _find_backrefs_not_to_self(_object: Any) -> list[str]:
|
||||
import objgraph # pylint: disable=import-outside-toplevel
|
||||
|
||||
return [
|
||||
_safe_repr(backref)
|
||||
for backref in objgraph.find_backref_chain(
|
||||
_object, lambda obj: obj is not _object
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def _log_object_sources(
|
||||
max_objects: int, last_ids: set[int], last_stats: dict[str, int]
|
||||
) -> None:
|
||||
# Imports deferred to avoid loading modules
|
||||
# in memory since usually only one part of this
|
||||
# integration is used at a time
|
||||
import gc # pylint: disable=import-outside-toplevel
|
||||
|
||||
gc.collect()
|
||||
|
||||
objects = gc.get_objects()
|
||||
new_objects: list[object] = []
|
||||
new_objects_overflow: dict[str, int] = {}
|
||||
current_ids = set()
|
||||
new_stats: dict[str, int] = {}
|
||||
had_new_object_growth = False
|
||||
try:
|
||||
for _object in objects:
|
||||
object_type = type(_object).__name__
|
||||
new_stats[object_type] = new_stats.get(object_type, 0) + 1
|
||||
|
||||
for _object in objects:
|
||||
id_ = id(_object)
|
||||
current_ids.add(id_)
|
||||
if id_ in last_ids:
|
||||
continue
|
||||
object_type = type(_object).__name__
|
||||
if last_stats.get(object_type, 0) < new_stats[object_type]:
|
||||
if len(new_objects) < max_objects:
|
||||
new_objects.append(_object)
|
||||
else:
|
||||
new_objects_overflow.setdefault(object_type, 0)
|
||||
new_objects_overflow[object_type] += 1
|
||||
|
||||
for _object in new_objects:
|
||||
had_new_object_growth = True
|
||||
object_type = type(_object).__name__
|
||||
_LOGGER.critical(
|
||||
"New object %s (%s/%s) at %s: %s",
|
||||
object_type,
|
||||
last_stats.get(object_type, 0),
|
||||
new_stats[object_type],
|
||||
_get_function_absfile(_object) or _find_backrefs_not_to_self(_object),
|
||||
_safe_repr(_object),
|
||||
)
|
||||
|
||||
for object_type, count in last_stats.items():
|
||||
new_stats[object_type] = max(new_stats.get(object_type, 0), count)
|
||||
finally:
|
||||
# Break reference cycles
|
||||
del objects
|
||||
del new_objects
|
||||
last_ids.clear()
|
||||
last_ids.update(current_ids)
|
||||
last_stats.clear()
|
||||
last_stats.update(new_stats)
|
||||
del new_stats
|
||||
del current_ids
|
||||
|
||||
if new_objects_overflow:
|
||||
_LOGGER.critical("New objects overflowed by %s", new_objects_overflow)
|
||||
elif not had_new_object_growth:
|
||||
_LOGGER.critical("No new object growth found")
|
||||
|
||||
@@ -25,7 +25,7 @@ memory:
|
||||
max: 3600
|
||||
unit_of_measurement: seconds
|
||||
start_log_objects:
|
||||
name: Start log objects
|
||||
name: Start logging objects
|
||||
description: Start logging growth of objects in memory
|
||||
fields:
|
||||
scan_interval:
|
||||
@@ -38,7 +38,7 @@ start_log_objects:
|
||||
max: 3600
|
||||
unit_of_measurement: seconds
|
||||
stop_log_objects:
|
||||
name: Stop log objects
|
||||
name: Stop logging objects
|
||||
description: Stop logging growth of objects in memory.
|
||||
dump_log_objects:
|
||||
name: Dump log objects
|
||||
@@ -51,6 +51,31 @@ dump_log_objects:
|
||||
example: State
|
||||
selector:
|
||||
text:
|
||||
start_log_object_sources:
|
||||
name: Start logging object sources
|
||||
description: Start logging sources of new objects in memory
|
||||
fields:
|
||||
scan_interval:
|
||||
name: Scan interval
|
||||
description: The number of seconds between logging objects.
|
||||
default: 30.0
|
||||
selector:
|
||||
number:
|
||||
min: 1
|
||||
max: 3600
|
||||
unit_of_measurement: seconds
|
||||
max_objects:
|
||||
name: Maximum objects
|
||||
description: The maximum number of objects to log.
|
||||
default: 5
|
||||
selector:
|
||||
number:
|
||||
min: 1
|
||||
max: 30
|
||||
unit_of_measurement: objects
|
||||
stop_log_object_sources:
|
||||
name: Stop logging object sources
|
||||
description: Stop logging sources of new objects in memory.
|
||||
lru_stats:
|
||||
name: Log LRU stats
|
||||
description: Log the stats of all lru caches.
|
||||
|
||||
@@ -38,7 +38,7 @@ BUTTONS: dict[str, tuple[PrusaLinkButtonEntityDescription, ...]] = {
|
||||
"printer": (
|
||||
PrusaLinkButtonEntityDescription[PrinterInfo](
|
||||
key="printer.cancel_job",
|
||||
name="Cancel Job",
|
||||
translation_key="cancel_job",
|
||||
icon="mdi:cancel",
|
||||
press_fn=lambda api: cast(Coroutine, api.cancel_job()),
|
||||
available_fn=lambda data: any(
|
||||
@@ -48,7 +48,7 @@ BUTTONS: dict[str, tuple[PrusaLinkButtonEntityDescription, ...]] = {
|
||||
),
|
||||
PrusaLinkButtonEntityDescription[PrinterInfo](
|
||||
key="job.pause_job",
|
||||
name="Pause Job",
|
||||
translation_key="pause_job",
|
||||
icon="mdi:pause",
|
||||
press_fn=lambda api: cast(Coroutine, api.pause_job()),
|
||||
available_fn=lambda data: (
|
||||
@@ -58,7 +58,7 @@ BUTTONS: dict[str, tuple[PrusaLinkButtonEntityDescription, ...]] = {
|
||||
),
|
||||
PrusaLinkButtonEntityDescription[PrinterInfo](
|
||||
key="job.resume_job",
|
||||
name="Resume Job",
|
||||
translation_key="resume_job",
|
||||
icon="mdi:play",
|
||||
press_fn=lambda api: cast(Coroutine, api.resume_job()),
|
||||
available_fn=lambda data: cast(bool, data["state"]["flags"]["paused"]),
|
||||
|
||||
@@ -24,7 +24,7 @@ class PrusaLinkJobPreviewEntity(PrusaLinkEntity, Camera):
|
||||
|
||||
last_path = ""
|
||||
last_image: bytes
|
||||
_attr_name = "Job Preview"
|
||||
_attr_translation_key = "job_preview"
|
||||
|
||||
def __init__(self, coordinator: JobUpdateCoordinator) -> None:
|
||||
"""Initialize a PrusaLink camera entity."""
|
||||
|
||||
@@ -65,7 +65,7 @@ SENSORS: dict[str, tuple[PrusaLinkSensorEntityDescription, ...]] = {
|
||||
),
|
||||
PrusaLinkSensorEntityDescription[PrinterInfo](
|
||||
key="printer.telemetry.temp-bed",
|
||||
name="Heatbed",
|
||||
translation_key="heatbed_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -74,7 +74,7 @@ SENSORS: dict[str, tuple[PrusaLinkSensorEntityDescription, ...]] = {
|
||||
),
|
||||
PrusaLinkSensorEntityDescription[PrinterInfo](
|
||||
key="printer.telemetry.temp-nozzle",
|
||||
name="Nozzle Temperature",
|
||||
translation_key="nozzle_temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
@@ -85,7 +85,7 @@ SENSORS: dict[str, tuple[PrusaLinkSensorEntityDescription, ...]] = {
|
||||
"job": (
|
||||
PrusaLinkSensorEntityDescription[JobInfo](
|
||||
key="job.progress",
|
||||
name="Progress",
|
||||
translation_key="progress",
|
||||
icon="mdi:progress-clock",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
value_fn=lambda data: cast(float, data["progress"]["completion"]) * 100,
|
||||
@@ -93,14 +93,14 @@ SENSORS: dict[str, tuple[PrusaLinkSensorEntityDescription, ...]] = {
|
||||
),
|
||||
PrusaLinkSensorEntityDescription[JobInfo](
|
||||
key="job.filename",
|
||||
name="Filename",
|
||||
translation_key="filename",
|
||||
icon="mdi:file-image-outline",
|
||||
value_fn=lambda data: cast(str, data["job"]["file"]["display"]),
|
||||
available_fn=lambda data: data.get("job") is not None,
|
||||
),
|
||||
PrusaLinkSensorEntityDescription[JobInfo](
|
||||
key="job.start",
|
||||
name="Print Start",
|
||||
translation_key="print_start",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
icon="mdi:clock-start",
|
||||
value_fn=ignore_variance(
|
||||
@@ -113,7 +113,7 @@ SENSORS: dict[str, tuple[PrusaLinkSensorEntityDescription, ...]] = {
|
||||
),
|
||||
PrusaLinkSensorEntityDescription[JobInfo](
|
||||
key="job.finish",
|
||||
name="Print Finish",
|
||||
translation_key="print_finish",
|
||||
icon="mdi:clock-end",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=ignore_variance(
|
||||
|
||||
@@ -25,6 +25,40 @@
|
||||
"pausing": "Pausing",
|
||||
"printing": "Printing"
|
||||
}
|
||||
},
|
||||
"heatbed_temperature": {
|
||||
"name": "Heatbed temperature"
|
||||
},
|
||||
"nozzle_temperature": {
|
||||
"name": "Nozzle temperature"
|
||||
},
|
||||
"progress": {
|
||||
"name": "Progress"
|
||||
},
|
||||
"filename": {
|
||||
"name": "Filename"
|
||||
},
|
||||
"print_start": {
|
||||
"name": "Print start"
|
||||
},
|
||||
"print_finish": {
|
||||
"name": "Print finish"
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
"cancel_job": {
|
||||
"name": "Cancel job"
|
||||
},
|
||||
"pause_job": {
|
||||
"name": "Pause job"
|
||||
},
|
||||
"resume_job": {
|
||||
"name": "Resume job"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"job_preview": {
|
||||
"name": "Preview"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ class Recorder(threading.Thread):
|
||||
self.hass,
|
||||
self._async_check_queue,
|
||||
timedelta(minutes=10),
|
||||
"Recorder queue watcher",
|
||||
name="Recorder queue watcher",
|
||||
)
|
||||
|
||||
@callback
|
||||
@@ -602,7 +602,7 @@ class Recorder(threading.Thread):
|
||||
self.hass,
|
||||
self._async_keep_alive,
|
||||
timedelta(seconds=KEEPALIVE_TIME),
|
||||
"Recorder keep alive",
|
||||
name="Recorder keep alive",
|
||||
)
|
||||
|
||||
# If the commit interval is not 0, we need to commit periodically
|
||||
@@ -611,7 +611,7 @@ class Recorder(threading.Thread):
|
||||
self.hass,
|
||||
self._async_commit,
|
||||
timedelta(seconds=self.commit_interval),
|
||||
"Recorder commit",
|
||||
name="Recorder commit",
|
||||
)
|
||||
|
||||
# Run nightly tasks at 4:12am
|
||||
|
||||
@@ -88,6 +88,8 @@ TABLE_STATISTICS_SHORT_TERM = "statistics_short_term"
|
||||
STATISTICS_TABLES = ("statistics", "statistics_short_term")
|
||||
|
||||
MAX_STATE_ATTRS_BYTES = 16384
|
||||
MAX_EVENT_DATA_BYTES = 32768
|
||||
|
||||
PSQL_DIALECT = SupportedDialect.POSTGRESQL
|
||||
|
||||
ALL_TABLES = [
|
||||
@@ -327,8 +329,18 @@ class EventData(Base):
|
||||
) -> bytes:
|
||||
"""Create shared_data from an event."""
|
||||
if dialect == SupportedDialect.POSTGRESQL:
|
||||
return json_bytes_strip_null(event.data)
|
||||
return json_bytes(event.data)
|
||||
bytes_result = json_bytes_strip_null(event.data)
|
||||
bytes_result = json_bytes(event.data)
|
||||
if len(bytes_result) > MAX_EVENT_DATA_BYTES:
|
||||
_LOGGER.warning(
|
||||
"Event data for %s exceed maximum size of %s bytes. "
|
||||
"This can cause database performance issues; Event data "
|
||||
"will not be stored",
|
||||
event.event_type,
|
||||
MAX_EVENT_DATA_BYTES,
|
||||
)
|
||||
return b"{}"
|
||||
return bytes_result
|
||||
|
||||
@staticmethod
|
||||
@lru_cache
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user