Merge pull request #23 from schizza/Heatindex

Add Heat Index
pull/24/head^2
Lukas Svoboda 2024-05-09 14:18:12 +02:00 committed by GitHub
commit 3229c40387
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 77 additions and 6 deletions

View File

@ -69,6 +69,7 @@ INDOOR_HUMIDITY: Final = "indoor_humidity"
UV: Final = "uv" UV: Final = "uv"
CH2_TEMP: Final = "ch2_temp" CH2_TEMP: Final = "ch2_temp"
CH2_HUMIDITY: Final = "ch2_humidity" CH2_HUMIDITY: Final = "ch2_humidity"
HEAT_INDEX: Final = "heat_index"
REMAP_ITEMS: dict = { REMAP_ITEMS: dict = {

View File

@ -37,6 +37,7 @@ from .const import (
DAILY_RAIN, DAILY_RAIN,
DEW_POINT, DEW_POINT,
DOMAIN, DOMAIN,
HEAT_INDEX,
INDOOR_HUMIDITY, INDOOR_HUMIDITY,
INDOOR_TEMP, INDOOR_TEMP,
OUTSIDE_HUMIDITY, OUTSIDE_HUMIDITY,
@ -51,7 +52,7 @@ from .const import (
WIND_SPEED, WIND_SPEED,
UnitOfDir, UnitOfDir,
) )
from .utils import wind_dir_to_text from .utils import wind_dir_to_text, heat_index
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -215,6 +216,16 @@ SENSOR_TYPES: tuple[WeatherSensorEntityDescription, ...] = (
translation_key=CH2_HUMIDITY, translation_key=CH2_HUMIDITY,
value_fn=lambda data: cast(int, data), value_fn=lambda data: cast(int, data),
), ),
WeatherSensorEntityDescription(
key=HEAT_INDEX,
native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.TEMPERATURE,
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny",
translation_key=HEAT_INDEX,
value_fn=lambda data: cast(int, data),
),
) )
@ -234,6 +245,8 @@ async def async_setup_entry(
if sensors_to_load := config_entry.options.get(SENSORS_TO_LOAD): if sensors_to_load := config_entry.options.get(SENSORS_TO_LOAD):
if WIND_DIR in sensors_to_load: if WIND_DIR in sensors_to_load:
sensors_to_load.append(WIND_AZIMUT) sensors_to_load.append(WIND_AZIMUT)
if (WIND_SPEED in sensors_to_load) and (OUTSIDE_TEMP in sensors_to_load):
sensors_to_load.append(HEAT_INDEX)
sensors = [ sensors = [
WeatherSensor(hass, description, coordinator) WeatherSensor(hass, description, coordinator)
for description in SENSOR_TYPES for description in SENSOR_TYPES
@ -293,6 +306,9 @@ class WeatherSensor(
if self.coordinator.data and (WIND_AZIMUT in self.entity_description.key): if self.coordinator.data and (WIND_AZIMUT in self.entity_description.key):
return self.entity_description.value_fn(self.coordinator.data.get(WIND_DIR)) return self.entity_description.value_fn(self.coordinator.data.get(WIND_DIR))
if self.coordinator.data and (HEAT_INDEX in self.entity_description.key):
return self.entity_description.value_fn(heat_index(self.coordinator.data))
return self.entity_description.value_fn(self._data) return self.entity_description.value_fn(self._data)
@property @property

View File

@ -76,6 +76,7 @@
"solar_radiation": { "name": "Sluneční osvit" }, "solar_radiation": { "name": "Sluneční osvit" },
"ch2_temp": { "name": "Teplota senzoru 2" }, "ch2_temp": { "name": "Teplota senzoru 2" },
"ch2_humidity": { "name": "Vlhkost sensoru 2" }, "ch2_humidity": { "name": "Vlhkost sensoru 2" },
"heat_index": { "name": "Tepelný index" },
"wind_azimut": { "wind_azimut": {
"name": "Azimut", "name": "Azimut",
"state": { "state": {

View File

@ -77,6 +77,7 @@
"solar_radiation": { "name": "Solar irradiance" }, "solar_radiation": { "name": "Solar irradiance" },
"ch2_temp": { "name": "Channel 2 temperature" }, "ch2_temp": { "name": "Channel 2 temperature" },
"ch2_humidity": { "name": "Channel 2 humidity" }, "ch2_humidity": { "name": "Channel 2 humidity" },
"heat_index": { "name": "Apparent temperature" },
"wind_azimut": { "wind_azimut": {
"name": "Bearing", "name": "Bearing",
"state": { "state": {

View File

@ -6,18 +6,31 @@ from homeassistant.components import persistent_notification
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.translation import async_get_translations from homeassistant.helpers.translation import async_get_translations
from homeassistant.const import UnitOfTemperature
from typing import Any
import math
import numpy as np
from .const import AZIMUT, DEV_DBG, REMAP_ITEMS, SENSORS_TO_LOAD, UnitOfDir from .const import (
AZIMUT,
DEV_DBG,
REMAP_ITEMS,
SENSORS_TO_LOAD,
UnitOfDir,
OUTSIDE_TEMP,
OUTSIDE_HUMIDITY,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
async def translations( async def translations(
hass: HomeAssistant, hass: HomeAssistant,
translation_domain: str, translation_domain: str,
translation_key: str, translation_key: str,
*, *,
key: str = "message", key: str = "message",
category: str = "notify" category: str = "notify",
) -> str: ) -> str:
"""Get translated keys for domain.""" """Get translated keys for domain."""
@ -31,6 +44,7 @@ async def translations(
if localize_key in _translations: if localize_key in _translations:
return _translations[localize_key] return _translations[localize_key]
async def translated_notification( async def translated_notification(
hass: HomeAssistant, hass: HomeAssistant,
translation_domain: str, translation_domain: str,
@ -39,13 +53,15 @@ async def translated_notification(
notification_id: str | None = None, notification_id: str | None = None,
*, *,
key: str = "message", key: str = "message",
category: str = "notify" category: str = "notify",
) -> str: ) -> str:
"""Translate notification.""" """Translate notification."""
localize_key = f"component.{translation_domain}.{category}.{translation_key}.{key}" localize_key = f"component.{translation_domain}.{category}.{translation_key}.{key}"
localize_title = f"component.{translation_domain}.{category}.{translation_key}.title" localize_title = (
f"component.{translation_domain}.{category}.{translation_key}.title"
)
language = hass.config.language language = hass.config.language
@ -97,10 +113,16 @@ def remap_items(entities):
return items return items
def loaded_sensors(config_entry: ConfigEntry) -> list | None: def loaded_sensors(config_entry: ConfigEntry) -> list | None:
"""Get loaded sensors.""" """Get loaded sensors."""
return config_entry.options.get(SENSORS_TO_LOAD) if config_entry.options.get(SENSORS_TO_LOAD) else [] return (
config_entry.options.get(SENSORS_TO_LOAD)
if config_entry.options.get(SENSORS_TO_LOAD)
else []
)
def check_disabled( def check_disabled(
hass: HomeAssistant, items, config_entry: ConfigEntry hass: HomeAssistant, items, config_entry: ConfigEntry
@ -129,6 +151,7 @@ def check_disabled(
return missing_sensors if entityFound else None return missing_sensors if entityFound else None
def wind_dir_to_text(deg: float) -> UnitOfDir | None: def wind_dir_to_text(deg: float) -> UnitOfDir | None:
"""Return wind direction in text representation. """Return wind direction in text representation.
@ -141,3 +164,32 @@ def wind_dir_to_text(deg: float) -> UnitOfDir | None:
return None return None
def heat_index(data: Any) -> UnitOfTemperature:
"""Calculate heat index from temperature."""
temp = float(data[OUTSIDE_TEMP])
rh = float(data[OUTSIDE_HUMIDITY])
adjustment = None
simple = 0.5 * (temp + 61.0 + ((temp - 68.0) * 1.2) + (rh * 0.094))
if ((simple + temp) / 2) > 80:
full_index = (
-42.379
+ 2.04901523 * temp
+ 10.14333127 * rh
- 0.22475541 * temp * rh
- 0.00683783 * temp * temp
- 0.05481717 * rh * rh
+ 0.00122874 * temp * temp * rh
+ 0.00085282 * temp * rh * rh
- 0.00000199 * temp * temp * rh * rh
)
if rh < 13 and (temp in np.arange(80, 112, 0.1)):
adjustment = ((13 - rh) / 4) * math.sqrt((17 - abs(temp - 95)) / 17)
if rh > 80 and (temp in np.arange(80, 87, 0.1)):
adjustment = ((rh - 85) / 10) * ((87 - temp) / 5)
return full_index + adjustment if adjustment else full_index
return simple