Compare commits

...

91 Commits

Author SHA1 Message Date
Franck Nijhof cff493fb98 2023.4.0 (#90855) 2023-04-05 19:57:42 +02:00
Franck Nijhof d67265bb66 Bumped version to 2023.4.0 2023-04-05 17:37:57 +02:00
Erik Montnemery 6e51f0d6f5 Adjust OTBR channel conflict URL (#90847) 2023-04-05 17:37:06 +02:00
Bram Kragten 82977f33ed Bump frontend to 20230405.0 (#90841) 2023-04-05 17:37:03 +02:00
epenet fb2d432d32 Adjust async_track_time_interval name argument (#90838)
Adjust async_track_time_interval naming
2023-04-05 17:36:59 +02:00
Tom Puttemans 0d019a3c4c Support entity name translation in DSMR Reader component (#90836)
* Use translation_key instead of name for the entity names and enum values

This change allows for the translation of entity names and their values based on a key, instead of having the English text in the code

* Adjusted tariff options order

Not really wrong, but this way it is consistent with all other entities
2023-04-05 17:36:55 +02:00
Paul Bottein 65b877bb77 Add entity name translations to prusalink entities (#90833) 2023-04-05 17:36:52 +02:00
Jan Bouwhuis 2a23583d67 Suppress imap logging on reconnect and presume state (#90826) 2023-04-05 17:36:48 +02:00
Penny Wood 80fe5051b3 Master RAS zone (#90825)
Fixes issue in some systems with different numbering systems
2023-04-05 17:36:44 +02:00
J. Nick Koston 2dfe33d177 Bump aioesphomeapi to 10.6.1 (#90816) 2023-04-05 17:36:41 +02:00
J. Nick Koston 617037a92d Fix BLEDevice not getting updated when details change for remote scanners (#90815) 2023-04-05 17:36:36 +02:00
Paulus Schoutsen 8f60a2bdd4 Bumped version to 2023.4.0b7 2023-04-04 21:09:22 -04:00
Patrick ZAJDA 9f7b2ba6c1 Add entity name translations to Broadlink sensors (#90783)
* Add entity name translations to Broadlink sensors

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

* Update tests

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

* Apply suggestions from code review

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

---------

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2023-04-04 21:09:10 -04:00
Patrick ZAJDA af34e25c89 Add translations for Nuki entity name and battery critical state attribute (#90772)
* Add translations for Nuki entity name and battery critical state attribute

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

* Remove door sensor name

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

---------

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>
2023-04-04 21:09:09 -04:00
Pascal Reeb c43dc37713 Add Warning in the issue registry if a HTTPS webhook is used for Nuki (#90718)
feat(nuki): create issue when https webhook URL was created
2023-04-04 21:09:08 -04:00
Maciej Bieniek 0d6177dbdb Address late review for NextDNS entity name translations (#90771) 2023-04-04 21:07:21 -04:00
starkillerOG f03b9036c5 Add async_write_ha_state to Reolink select (#90764)
Add async_write_ha_state to select
2023-04-04 21:07:20 -04:00
Jan Bouwhuis 1848a723cd Fix recovering imap connection triggers re-auth (#90762) 2023-04-04 21:07:19 -04:00
Erik Montnemery 8230a52e0a Update template environment from the event loop (#90758) 2023-04-04 21:07:18 -04:00
Paulus Schoutsen d0e9470c7c Fix frontend test again (#90754) 2023-04-04 21:07:17 -04:00
J. Nick Koston b50354f362 Add render count to templates repr (#90753) 2023-04-04 21:07:16 -04:00
J. Nick Koston e4b3a146be Bump aiohomekit to 2.6.3 (#90752) 2023-04-04 21:07:15 -04:00
TheJulianJES 1861a621b2 Restore state for ZHA OnOff binary sensors (#90749)
* Restore state for ZHA OnOff binary sensors

* Let `Motion` extend `Opening`

`Motion` is just a specified version of `Opening` that only changes the device class for some motion sensors.
Since we have more "special code" in the OnOff/Opening sensor now, we also want to make sure that gets applied to `Motion` binary sensors.

* Improve comment and type

* Add test to verify that binary sensors restore last HA state
2023-04-04 21:07:14 -04:00
J. Nick Koston 0746e09256 Prevent overly large event data from being stored in the database (#90747)
This is the same change as #87105 for events
2023-04-04 21:07:13 -04:00
J. Nick Koston 0166cd082b Bump zeroconf to 0.54.0 (#90744)
* Bump zeroconf to 0.54.0

fixes incorrect addresses when the server name changes

changelog: https://github.com/python-zeroconf/python-zeroconf/compare/0.53.0...0.54.0

* fix
2023-04-04 21:07:12 -04:00
Maciej Bieniek 0a74f946db Add entity name translations to NextDNS (#90743)
Add entity name translations
2023-04-04 21:07:11 -04:00
Mark Adkins d04b45a821 SharkIQ Hotfix - Handle current installations by using default REGION (#90741)
* Add default region on async_setup_entry

* Move logic to migration function

* Move update logic back to setup function, but updates the config if needed.

* Remove commented out code

* Update Tests & Config setting method

* Update homeassistant/components/sharkiq/__init__.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Update homeassistant/components/sharkiq/__init__.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Accept Suggestions & Formatting

---------

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2023-04-04 21:07:10 -04:00
Maciej Bieniek a5a6641bb4 Add entity name translations to Tractive (#90738)
Add entity name translations
2023-04-04 21:07:09 -04:00
Michael 1420cda837 Add entity name translations to SMS (#90727) 2023-04-04 21:07:08 -04:00
Fabio De Simone cba5751ca2 Fix bluetooth_le_tracker reporting devices Home when they leave (#90641)
* fix bluetooth_le_tracker reporting devices Home when they leave

* refactor

* implement tests for BLE service_info.time check

* update bluetooth_le_tracker tests

* tweaks

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
2023-04-04 21:07:07 -04:00
Paulus Schoutsen a3e66b5dde Bumped version to 2023.4.0b6 2023-04-03 16:51:04 -04:00
Bram Kragten 83dd52ab1f Update frontend to 20230403.0 (#90735) 2023-04-03 16:51:01 -04:00
Michael da1e5f6a3c Add entity name translations to sun (#90732) 2023-04-03 16:51:00 -04:00
Michael 8f9868024c Add entity name translations to Luftdaten (#90725) 2023-04-03 16:50:59 -04:00
Erik Montnemery c90396cd57 Bump pychromecast to 13.0.7 (#90724) 2023-04-03 16:50:58 -04:00
Aaron Bach 509c1ca99c Fix missing battery sensors for SimpliSafe locks (#90722) 2023-04-03 16:50:57 -04:00
Michael 431fbee641 Fix translation of status binary sensor in PI-Hole (#90719) 2023-04-03 16:50:56 -04:00
Michael 28983bca85 Add entity name translations to Pi-hole (#90713) 2023-04-03 16:50:55 -04:00
Felix Rotthowe 601498617d Fix Livisi climate min/max temperature (#90712)
* Correctly set livisi climate min/max temp

* fix imports
2023-04-03 16:50:54 -04:00
Michael 6c208f655d Add entity name translations to NUT (#90709) 2023-04-03 16:50:53 -04:00
Michael eaaf24d326 Add entity name translations to AVM Fritz!SmartHome (#90707)
* add entity name translation

* sort and capitalize

* adjust tests

* sort entities
2023-04-03 16:50:52 -04:00
Michael 0c12d45581 Add entity name translations to Synology DSM (#90706)
* add entity name translation

* sort strings

* sort and capitalize strings
2023-04-03 16:50:51 -04:00
Michael c2e46db76d Add entity name translations to AVM Fritz!Tools (#90703)
* add entity name translation

* apply suggestions

* sort strings
2023-04-03 16:50:50 -04:00
Maciej Bieniek 47c8b7804d Add entity name translations to BraviaTV (#90702)
Add entity name translations
2023-04-03 16:50:49 -04:00
Maciej Bieniek 8d302aea9e Use the default entity names in GIOS (#90700) 2023-04-03 16:50:48 -04:00
Stephan Uhle 3a73425888 Fix ha version in EDL21 deprecation warning (#90699)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2023-04-03 16:50:47 -04:00
epenet f9e4fe016f Use entity name translations in SFR Box (#90698) 2023-04-03 16:50:46 -04:00
epenet 5835ae03bc Use entity name translations in Renault (#90697) 2023-04-03 16:50:45 -04:00
epenet 71608d4795 Use entity name translations in 1-wire (#90696)
* Use entity name translations in onewire

* Adjust binary sensors

* Adjust switches

* Cleanup
2023-04-03 16:50:44 -04:00
Maciej Bieniek e38590e40a Use the default entity names in Airly (#90693) 2023-04-03 16:50:43 -04:00
Maciej Bieniek 9e3b54f539 Add entity name translations to NAM (#90681) 2023-04-03 16:50:42 -04:00
J. Nick Koston 24ff2ddae5 Ensure system log does not raise while processing logger messages (#90652) 2023-04-03 16:50:41 -04:00
Michael Davie 621de8bb5f Bump env_canada to v0.5.30 (#90644) 2023-04-03 16:50:40 -04:00
Patrick ZAJDA 6cbf9288b5 Add entity name translations to Switchbot (#90600)
* Add entity name translations to Switchbot

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

* Apply suggestions from code review

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

* Fix tests

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

* Update homeassistant/components/switchbot/strings.json

Co-authored-by: Patrick ZAJDA <patrick@zajda.fr>

---------

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2023-04-03 16:50:39 -04:00
Franck Nijhof 9f95da7793 Add entity name translations to Plugwise (#90537)
* Add entity name translations to Plugwise

* Re-use extisting translation where possible
2023-04-03 16:50:38 -04:00
Nerdix cb5326b798 Correct handling if WIFI combine suffix is "None" (#90528)
* Correct handling of "None" WIFI combine suffix

* Update tests/components/unifi/test_config_flow.py

Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>

* Update tests/components/unifi/test_config_flow.py

Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>

---------

Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
2023-04-03 16:50:37 -04:00
Erik Montnemery 1aa6d3e896 Raise repair issue if OTBR and ZHA are on different channels (#90494)
* Raise repair issue if OTBR and ZHA are on different channels

* Update issues after creating or setting dataset

* Explain impact

* Add link to documentation, adjust language

* Update homeassistant/components/otbr/strings.json

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2023-04-03 16:50:36 -04:00
Matthias Alphart 1c8d4b8bb8 Use entity name translations in Fronius (#90463) 2023-04-03 16:50:35 -04:00
G Johansson 8669ee3685 Remove Darksky integration (#90322) 2023-04-03 16:50:34 -04:00
Paulus Schoutsen 20d8bbbd0c Bumped version to 2023.4.0b5 2023-04-02 20:57:16 -04:00
J. Nick Koston e10e3ee7cc Fix memory churn in state templates (#90685)
* Fix memory churn in state templates

The LRU for state templates was limited to 512 states. As soon
as it was exaused, system performance would tank as each template
that iterated all states would have to create and GC any state
> 512

* does it scale?

* avoid copy on all

* comment

* preen

* cover

* cover

* comments

* comments

* comments

* preen

* preen
2023-04-02 20:57:07 -04:00
Maciej Bieniek 83b7018be2 Fix default sensor entity name for PM1 (#90684)
Fix PM1 text
2023-04-02 20:57:06 -04:00
J. Nick Koston 6d967ac535 Bump zeroconf to 0.53.0 (#90682) 2023-04-02 20:57:05 -04:00
Paulus Schoutsen 77bc745bed Fix frontend test (#90679) 2023-04-02 20:57:04 -04:00
Patrick ZAJDA 8fe7b01baa Add entity name translations for Nest sensors (#90677)
Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>
2023-04-02 20:57:04 -04:00
J. Nick Koston 5e5888b37a Bump zeroconf to 0.52.0 (#90660)
* Bump zeroconf to 0.52.0

Switch to using the new ip_addresses_by_version which avoids
all the ip address conversions

* updates
2023-04-02 20:57:03 -04:00
Maciej Bieniek 90de51fff3 Add entity name translations to Airly (#90656)
Add entity name translations
2023-04-02 20:55:38 -04:00
Maciej Bieniek 89230b75be Add entity name translations to GIOS (#90655)
* Add entity name translations

* Update tests
2023-04-02 20:55:37 -04:00
J. Nick Koston cbe3cabf0a Add object source logger to profiler (#90650)
* Add object source logger to profiler

* fixes

* cleanup

* tweaks

* logging

* logging

* too intensive

* adjust

* Update homeassistant/bootstrap.py

* fixes

* fixes

* coverage
2023-04-02 20:55:36 -04:00
Maciej Bieniek c259c1afe3 Add entity name translations to Brother (#90634)
* Add entity name translations

* Fix sensor name

* Update tests

* Suggested change
2023-04-02 20:55:36 -04:00
mletenay 1ff93518b5 Update goodwe library to v0.2.30 (#90607) 2023-04-02 20:55:34 -04:00
Paulus Schoutsen aa6cf3d208 Bumped version to 2023.4.0b4 2023-04-01 15:23:53 -04:00
Bram Kragten 2a28d40dc8 Update frontend to 20230401.0 (#90646) 2023-04-01 15:23:45 -04:00
Jan Bouwhuis c006b3b1df Fix mqtt device_tracker is not reloading yaml (#90639) 2023-04-01 15:23:44 -04:00
nono bacd77a03a Fix Rest switch init was not retrying if unreachable at setup (#90627)
* Fix Rest switch init was not retrying if unreachable at setup

* pass error log to platformnotready
prevents spamming the same message in logs.
2023-04-01 15:23:43 -04:00
J. Nick Koston 75694307e2 Bump zeroconf to 0.51.0 (#90622)
* Bump zeroconf to 0.50.0

changelog: https://github.com/python-zeroconf/python-zeroconf/compare/0.47.4...0.50.0

* bump to 51
2023-04-01 15:23:42 -04:00
J. Nick Koston 1189b2ad70 Small speed up to _collection_changed (#90621)
attrgetter builds a fast method which happens in native code
https://github.com/python/cpython/blob/4664a7cf689946f0c9854cadee7c6aa9c276a8cf/Modules/_operator.c#L1413
2023-04-01 15:23:42 -04:00
Joakim Sørensen d5d5bb0732 Only limit stats to started add-ons (#90611) 2023-04-01 15:23:41 -04:00
J. Nick Koston 6242dd2214 Avoid sorting domain/all states in templates (#90608) 2023-04-01 15:23:40 -04:00
Paulus Schoutsen 03f085d7be Bumped version to 2023.4.0b3 2023-03-31 15:41:37 -04:00
Raman Gupta b3348c3e6f Bump zwave-js-server-python to 0.47.3 (#90606)
* Bump zwave-js-server-python to 0.47.2

* Bump zwave-js-server-python to 0.47.3
2023-03-31 15:41:33 -04:00
puddly 590db0fa74 Perform an energy scan when downloading ZHA diagnostics (#90605) 2023-03-31 15:41:32 -04:00
puddly f56ccf90d9 Fix ZHA definition error on received command (#90602)
* Fix use of deprecated command schema access

* Add a unit test
2023-03-31 15:41:31 -04:00
Bram Kragten c63f8e714e Update frontend to 20230331.0 (#90594) 2023-03-31 15:41:30 -04:00
starkillerOG a20771f571 Bump reolink-aio to 0.5.9 (#90590) 2023-03-31 15:41:29 -04:00
Franck Nijhof 2d482f1f57 Raise on invalid (dis)arm code in manual mqtt alarm (#90584) 2023-03-31 15:41:28 -04:00
Erik Montnemery 499962f4ee Tweak yalexs_ble translations (#90582) 2023-03-31 15:41:27 -04:00
Franck Nijhof 88a407361c Raise on invalid (dis)arm code in manual alarm (#90579) 2023-03-31 15:41:26 -04:00
Franck Nijhof 89dc6db5a7 Add arming/disarming state to Verisure (#90577) 2023-03-31 15:41:25 -04:00
J. Nick Koston de9e7e47fe Make sonos activity check a background task (#90553)
Ensures the task is canceled at shutdown if the device
is offline and the ping is still in progress
2023-03-31 15:41:24 -04:00
epenet ab66664f20 Allow removal of sensor settings in scrape (#90412)
* Allow removal of sensor settings in scrape

* Adjust

* Adjust

* Add comment

* Simplify

* Simplify

* Adjust

* Don't allow empty string

* Only allow None

* Use default as None

* Use sentinel "none"

* Not needed

* Adjust unit of measurement

* Add translation keys for "none"

* Use translations

* Sort

* Add enum and timestamp

* Use translation references

* Remove default and set suggested_values

* Disallow enum device class

* Adjust tests

* Adjust _strip_sentinel
2023-03-31 15:41:23 -04:00
215 changed files with 4775 additions and 3091 deletions
-2
View File
@@ -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
+1
View File
@@ -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),
+11 -11
View File
@@ -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(
+1 -1
View File
@@ -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",
)
)
+2 -2
View File
@@ -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"
}
}
}
}
+10 -10
View File
@@ -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"
}
}
}
}
+34 -34
View File
@@ -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"
}
}
}
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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"]
}
-927
View File
@@ -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()
-281
View File
@@ -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))
+4 -1
View File
@@ -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"
}
}
}
}
+1 -1
View File
@@ -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),
+7 -6
View File
@@ -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:
-1
View File
@@ -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
+16 -16
View File
@@ -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."""
+13 -13
View File
@@ -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%]"
}
}
}
}
+1 -76
View File
@@ -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"]
}
+7 -13
View File
@@ -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"]
}
+9 -7
View File
@@ -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."]
}
+12 -13
View File
@@ -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:
+4 -1
View File
@@ -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
+6 -2
View File
@@ -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(
+6 -6
View File
@@ -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]:
+1
View File
@@ -113,6 +113,7 @@ RELOADABLE_PLATFORMS = [
Platform.CAMERA,
Platform.CLIMATE,
Platform.COVER,
Platform.DEVICE_TRACKER,
Platform.FAN,
Platform.HUMIDIFIER,
Platform.LIGHT,
+1 -1
View File
@@ -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,
)
+31 -34
View File
@@ -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,
+111 -1
View File
@@ -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"
}
}
}
+2 -2
View File
@@ -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,
),
+1 -1
View File
@@ -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,
)
+25 -25
View File
@@ -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"
}
}
}
}
+64 -64
View File
@@ -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",
+35 -12
View File
@@ -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
+1
View File
@@ -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:
+1 -1
View File
@@ -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%]"
}
}
}
}
+82 -81
View File
@@ -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)},
+84
View File
@@ -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
+30 -31
View File
@@ -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"
+10 -12
View File
@@ -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,
)
)
+3 -117
View File
@@ -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 -1
View File
@@ -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}."
}
}
}
+186
View File
@@ -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:
+11 -11
View File
@@ -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" }
}
}
}
+5 -5
View File
@@ -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,
+1 -1
View File
@@ -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",
+3 -5
View File
@@ -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",
+64 -8
View File
@@ -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"
}
}
}
+3 -3
View File
@@ -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(
+173 -23
View File
@@ -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.
+3 -3
View File
@@ -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"]),
+1 -1
View File
@@ -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."""
+6 -6
View File
@@ -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"
}
}
}
+3 -3
View File
@@ -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
+14 -2
View File
@@ -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