From 712b984833f7c3446c7ca1b8c6818063be52005a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Matheson=20Wergeland?= Date: Mon, 24 Oct 2022 01:55:22 +0200 Subject: [PATCH] =?UTF-8?q?Support=20Nob=C3=B8=20Switch=20as=20temperature?= =?UTF-8?q?=20sensor=20(#78480)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Expose Nobø Switch as temperatur sensor. * - Review - Hub may report current temperature as None * Avoid update during entity addition, and fix race condition * Update pynobo to 1.6.0 Use new method to fix potential race condition. * Use generator expressions --- .coveragerc | 1 + homeassistant/components/nobo_hub/__init__.py | 10 +- homeassistant/components/nobo_hub/climate.py | 6 +- homeassistant/components/nobo_hub/const.py | 1 + .../components/nobo_hub/manifest.json | 2 +- homeassistant/components/nobo_hub/sensor.py | 95 +++++++++++++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 8 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 homeassistant/components/nobo_hub/sensor.py diff --git a/.coveragerc b/.coveragerc index 62083486c03..08607ace1f7 100644 --- a/.coveragerc +++ b/.coveragerc @@ -860,6 +860,7 @@ omit = homeassistant/components/noaa_tides/sensor.py homeassistant/components/nobo_hub/__init__.py homeassistant/components/nobo_hub/climate.py + homeassistant/components/nobo_hub/sensor.py homeassistant/components/norway_air/air_quality.py homeassistant/components/notify_events/notify.py homeassistant/components/notion/__init__.py diff --git a/homeassistant/components/nobo_hub/__init__.py b/homeassistant/components/nobo_hub/__init__.py index 7db9eb96f7e..d828fb78b78 100644 --- a/homeassistant/components/nobo_hub/__init__.py +++ b/homeassistant/components/nobo_hub/__init__.py @@ -1,8 +1,6 @@ """The Nobø Ecohub integration.""" from __future__ import annotations -import logging - from pynobo import nobo from homeassistant.config_entries import ConfigEntry @@ -25,9 +23,7 @@ from .const import ( NOBO_MANUFACTURER, ) -PLATFORMS = [Platform.CLIMATE] - -_LOGGER = logging.getLogger(__name__) +PLATFORMS = [Platform.CLIMATE, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -37,7 +33,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: discover = entry.data[CONF_AUTO_DISCOVERED] ip_address = None if discover else entry.data[CONF_IP_ADDRESS] hub = nobo(serial=serial, ip=ip_address, discover=discover, synchronous=False) - await hub.start() + await hub.connect() hass.data.setdefault(DOMAIN, {}) @@ -65,6 +61,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry.async_on_unload(entry.add_update_listener(options_update_listener)) + await hub.start() + return True diff --git a/homeassistant/components/nobo_hub/climate.py b/homeassistant/components/nobo_hub/climate.py index ba38e0b1530..d138788fba0 100644 --- a/homeassistant/components/nobo_hub/climate.py +++ b/homeassistant/components/nobo_hub/climate.py @@ -69,10 +69,7 @@ async def async_setup_entry( ) # Add zones as entities - async_add_entities( - [NoboZone(zone_id, hub, override_type) for zone_id in hub.zones], - True, - ) + async_add_entities(NoboZone(zone_id, hub, override_type) for zone_id in hub.zones) class NoboZone(ClimateEntity): @@ -107,6 +104,7 @@ class NoboZone(ClimateEntity): ATTR_VIA_DEVICE: (DOMAIN, hub.hub_info[ATTR_SERIAL]), ATTR_SUGGESTED_AREA: hub.zones[zone_id][ATTR_NAME], } + self._read_state() async def async_added_to_hass(self) -> None: """Register callback from hub.""" diff --git a/homeassistant/components/nobo_hub/const.py b/homeassistant/components/nobo_hub/const.py index 320c2f43c07..ff0f25cfec3 100644 --- a/homeassistant/components/nobo_hub/const.py +++ b/homeassistant/components/nobo_hub/const.py @@ -17,3 +17,4 @@ ATTR_TEMP_ECO_C = "temp_eco_c" ATTR_OVERRIDE_ALLOWED = "override_allowed" ATTR_TARGET_TYPE = "target_type" ATTR_TARGET_ID = "target_id" +ATTR_ZONE_ID = "zone_id" diff --git a/homeassistant/components/nobo_hub/manifest.json b/homeassistant/components/nobo_hub/manifest.json index 14e10a1ffaf..0df92c4c5ae 100644 --- a/homeassistant/components/nobo_hub/manifest.json +++ b/homeassistant/components/nobo_hub/manifest.json @@ -3,7 +3,7 @@ "name": "Nob\u00f8 Ecohub", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/nobo_hub", - "requirements": ["pynobo==1.4.0"], + "requirements": ["pynobo==1.6.0"], "codeowners": ["@echoromeo", "@oyvindwe"], "iot_class": "local_push" } diff --git a/homeassistant/components/nobo_hub/sensor.py b/homeassistant/components/nobo_hub/sensor.py new file mode 100644 index 00000000000..fe33c6ee83e --- /dev/null +++ b/homeassistant/components/nobo_hub/sensor.py @@ -0,0 +1,95 @@ +"""Python Control of Nobø Hub - Nobø Energy Control.""" +from __future__ import annotations + +from pynobo import nobo + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + ATTR_IDENTIFIERS, + ATTR_MANUFACTURER, + ATTR_MODEL, + ATTR_NAME, + ATTR_SUGGESTED_AREA, + ATTR_VIA_DEVICE, + TEMP_CELSIUS, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import StateType + +from .const import ATTR_SERIAL, ATTR_ZONE_ID, DOMAIN, NOBO_MANUFACTURER + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up any temperature sensors connected to the Nobø Ecohub.""" + + # Setup connection with hub + hub: nobo = hass.data[DOMAIN][config_entry.entry_id] + + async_add_entities( + NoboTemperatureSensor(component["serial"], hub) + for component in hub.components.values() + if component[ATTR_MODEL].has_temp_sensor + ) + + +class NoboTemperatureSensor(SensorEntity): + """A Nobø device with a temperature sensor.""" + + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_native_unit_of_measurement = TEMP_CELSIUS + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_should_poll = False + + def __init__(self, serial: str, hub: nobo) -> None: + """Initialize the temperature sensor.""" + self._temperature: StateType = None + self._id = serial + self._nobo = hub + component = hub.components[self._id] + self._attr_unique_id = component[ATTR_SERIAL] + self._attr_name = "Temperature" + self._attr_has_entity_name = True + self._attr_device_info: DeviceInfo = { + ATTR_IDENTIFIERS: {(DOMAIN, component[ATTR_SERIAL])}, + ATTR_NAME: component[ATTR_NAME], + ATTR_MANUFACTURER: NOBO_MANUFACTURER, + ATTR_MODEL: component[ATTR_MODEL].name, + ATTR_VIA_DEVICE: (DOMAIN, hub.hub_info[ATTR_SERIAL]), + } + zone_id = component[ATTR_ZONE_ID] + if zone_id != "-1": + self._attr_device_info[ATTR_SUGGESTED_AREA] = hub.zones[zone_id][ATTR_NAME] + self._read_state() + + async def async_added_to_hass(self) -> None: + """Register callback from hub.""" + self._nobo.register_callback(self._after_update) + + async def async_will_remove_from_hass(self) -> None: + """Deregister callback from hub.""" + self._nobo.deregister_callback(self._after_update) + + @callback + def _read_state(self) -> None: + """Read the current state from the hub. This is a local call.""" + value = self._nobo.get_current_component_temperature(self._id) + if value is None: + self._attr_native_value = None + else: + self._attr_native_value = round(float(value), 1) + + @callback + def _after_update(self, hub) -> None: + self._read_state() + self.async_write_ha_state() diff --git a/requirements_all.txt b/requirements_all.txt index dd1a8e53270..25bedebc6fb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1742,7 +1742,7 @@ pynetio==0.1.9.1 pynina==0.1.8 # homeassistant.components.nobo_hub -pynobo==1.4.0 +pynobo==1.6.0 # homeassistant.components.nuki pynuki==1.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 72f67faf438..403cd562bce 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1228,7 +1228,7 @@ pynetgear==0.10.8 pynina==0.1.8 # homeassistant.components.nobo_hub -pynobo==1.4.0 +pynobo==1.6.0 # homeassistant.components.nuki pynuki==1.5.2