From 4eb260aef4b609c5d7824f3e312e6e9c5ba652c2 Mon Sep 17 00:00:00 2001 From: Boced66 Date: Thu, 10 Jun 2021 09:13:40 +0200 Subject: [PATCH 1/2] Add version (#21) For the compatibility with the future version of HASS --- custom_components/nhc2/manifest.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/custom_components/nhc2/manifest.json b/custom_components/nhc2/manifest.json index 3d3f2485..987a8a3d 100644 --- a/custom_components/nhc2/manifest.json +++ b/custom_components/nhc2/manifest.json @@ -5,5 +5,6 @@ "config_flow": true, "issue_tracker": "https://github.com/filipvh/hass-nhc2/issues", "documentation": "https://github.com/filipvh/hass-nhc2/blob/master/README.md", - "codeowners": ["@filipvh"] -} \ No newline at end of file + "codeowners": ["@filipvh"], + "version": "2021.4.1" +} From 6efc1b4ac3150b16dd58ddf47d843a510a3d971f Mon Sep 17 00:00:00 2001 From: joleys Date: Mon, 28 Jun 2021 12:20:29 +0200 Subject: [PATCH 2/2] add thermostats (#16) --- custom_components/nhc2/__init__.py | 33 ++--- custom_components/nhc2/climate.py | 214 +++++++++++++++++++++++++++ custom_components/nhc2/const.py | 3 +- custom_components/nhc2/helpers.py | 2 +- custom_components/nhc2/manifest.json | 2 +- 5 files changed, 231 insertions(+), 23 deletions(-) create mode 100644 custom_components/nhc2/climate.py diff --git a/custom_components/nhc2/__init__.py b/custom_components/nhc2/__init__.py index 7b43ea99..9e31f457 100644 --- a/custom_components/nhc2/__init__.py +++ b/custom_components/nhc2/__init__.py @@ -12,7 +12,7 @@ from .const import DOMAIN, KEY_GATEWAY, CONF_SWITCHES_AS_LIGHTS from .helpers import extract_versions -REQUIREMENTS = ['nhc2-coco==1.3.3'] +REQUIREMENTS = ['nhc2-coco==1.4.1'] _LOGGER = logging.getLogger(__name__) @@ -59,6 +59,13 @@ async def async_setup(hass, config): return True +FORWARD_PLATFORMS = ( + "climate", + "switch", + "light", + "fan", + "cover" +) async def async_setup_entry(hass, entry): """Create a NHC2 gateway.""" @@ -95,25 +102,11 @@ def process_sysinfo(nhc2_sysinfo): sw_version=nhc_version + ' - CoCo Image: ' + coco_image, ) - hass.async_create_task( - hass.config_entries.async_forward_entry_setup( - entry, 'light') - ) - - hass.async_create_task( - hass.config_entries.async_forward_entry_setup( - entry, 'switch') - ) - - hass.async_create_task( - hass.config_entries.async_forward_entry_setup( - entry, 'cover') - ) - - hass.async_create_task( - hass.config_entries.async_forward_entry_setup( - entry, 'fan') - ) + for platform in FORWARD_PLATFORMS: + _LOGGER.info("Forwarding platform: %s", platform) + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(entry, platform) + ) return process_sysinfo diff --git a/custom_components/nhc2/climate.py b/custom_components/nhc2/climate.py new file mode 100644 index 00000000..384b5f1b --- /dev/null +++ b/custom_components/nhc2/climate.py @@ -0,0 +1,214 @@ +"""Support for Niko Home Control II Thermostats.""" +import logging + +from homeassistant.components.climate import ( + ATTR_TEMPERATURE, + ClimateEntity +) + +from homeassistant.components.climate.const import ( + ATTR_FAN_MODE, + ATTR_HVAC_MODE, + ATTR_PRESET_MODE, + ATTR_SWING_MODE, + HVAC_MODE_COOL, + HVAC_MODE_DRY, + HVAC_MODE_FAN_ONLY, + HVAC_MODE_HEAT, + HVAC_MODE_HEAT_COOL, + HVAC_MODE_OFF, + CURRENT_HVAC_HEAT, + CURRENT_HVAC_COOL, + CURRENT_HVAC_IDLE, + PRESET_SLEEP, + PRESET_COMFORT, + PRESET_ECO, + PRESET_NONE, + SUPPORT_PRESET_MODE, + SUPPORT_TARGET_TEMPERATURE, + SUPPORT_TARGET_TEMPERATURE_RANGE +) + +from nhc2_coco import CoCo +from nhc2_coco.coco_climate import CoCoThermostat +from nhc2_coco.coco_device_class import CoCoDeviceClass + +from .const import DOMAIN, KEY_GATEWAY, BRAND, CLIMATE +from .helpers import nhc2_entity_processor + +KEY_GATEWAY = KEY_GATEWAY +KEY_ENTITY = 'nhc2_thermostats' + +_LOGGER = logging.getLogger(__name__) + +HA_STATE_TO_NHC2 = { + HVAC_MODE_COOL: "Cooling", + HVAC_MODE_HEAT: "Heating", + HVAC_MODE_OFF: "None" +} + +NHC2_TO_HA_STATE = { + "Cooling": HVAC_MODE_COOL, + "Heating": HVAC_MODE_HEAT, + "None": HVAC_MODE_OFF +} + +NHC2_TO_HA_CURRENT_STATE = { + "Cooling": CURRENT_HVAC_COOL, + "Heating": CURRENT_HVAC_HEAT, + "None": CURRENT_HVAC_IDLE +} + +HA_PRESET_TO_NHC2 = { + PRESET_NONE: "Off", + PRESET_ECO: "Eco", + PRESET_COMFORT: "Day", + PRESET_SLEEP: "Night" +} + +HVAC_MODES_LIST = list(HA_STATE_TO_NHC2) +FEATURE_LIST = SUPPORT_PRESET_MODE | SUPPORT_TARGET_TEMPERATURE + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Load NHC2 Thermostat basd on a Config Entry.""" + hass.data.setdefault(KEY_ENTITY, {})[config_entry.entry_id] = [] + gateway: CoCo = hass.data[KEY_GATEWAY][config_entry.entry_id] + _LOGGER.debug('Platform is starting') + gateway.get_devices(CoCoDeviceClass.THERMOSTATS, + nhc2_entity_processor(hass, + config_entry, + async_add_entities, + KEY_ENTITY, + lambda x: NHC2HassThermostat(x)) + ) + +class NHC2HassThermostat(ClimateEntity): + """Representation of a Niko Home Control II thermostat.""" + + def __init__(self, nhc2thermostat: CoCoThermostat): + """Initialize a thermostat.""" + self._nhc2thermostat = nhc2thermostat + self._current_temperature = nhc2thermostat.current_temperature + nhc2thermostat.on_change = self._on_change + _LOGGER.debug("Init new thermostat: %s", nhc2thermostat.name) + _LOGGER.info(self.name + " temperature: " + str(self._current_temperature)) + + def _on_change(self): + self.schedule_update_ha_state() + + def update_properties(self): + """Handle data changes for node values.""" + pass + + async def async_set_temperature(self, **kwargs): + """Set new target temperatures.""" + temp = float(kwargs.get(ATTR_TEMPERATURE)) + self._nhc2thermostat.set_temperature(temp) + self.schedule_update_ha_state() + + async def async_set_hvac_mode(self, hvac_mode): + """Set new target hvac mode.""" + pass + + async def async_set_preset_mode(self, preset_mode): + """Set new target preset mode.""" + self._nhc2thermostat.set_preset_mode(preset_mode) + self.schedule_update_ha_state() + + @property + def state(self): + """Return the current temperature.""" + return NHC2_TO_HA_STATE[self._nhc2thermostat.hvac_mode] + + @property + def current_temperature(self): + """Return the current temperature.""" + return self._current_temperature + + @property + def target_temperature(self): + """Return the target temperature.""" + return self._nhc2thermostat.target_temperature + + @property + def hvac_action(self): + """Return thermostat's current operation mode""" + return NHC2_TO_HA_CURRENT_STATE[self._nhc2thermostat.hvac_mode] + + @property + def hvac_mode(self): + """Return thermostat's operation mode""" + return NHC2_TO_HA_STATE[self._nhc2thermostat.hvac_mode] + + @property + def hvac_modes(self): + """Return the list of available hvac operation modes.""" + return HVAC_MODES_LIST + + @property + def preset_mode(self): + """Return the current preset mode, e.g., home, away, temp.""" + return self._nhc2thermostat.preset_mode + + @property + def preset_modes(self): + """Return the list off supported preset modes.""" + return list(self._nhc2thermostat.preset_modes) + + @property + def temperature_unit(self): + """Return the unit of measurement.""" + return self._nhc2thermostat.temperature_unit + + @property + def target_temperature_low(self): + """Return the minimum target temperature.""" + return self._nhc2thermostat.target_temperature_low + + @property + def target_temperature_high(self): + """Return the maximum target temperature.""" + return self._nhc2thermostat.target_temperature_high + + @property + def target_temperature_step(self): + """Return the target temperature step size.""" + return self._nhc2thermostat.target_temperature_step + + @property + def temp_min(self): + """Return the minimum temperature.""" + return self._nhc2thermostat.temp_min + + @property + def temp_max(self): + """Return the maximum temperature.""" + return self._nhc2thermostat.temp_max + + @property + def supported_features(self): + """Return the list of supported features.""" + return FEATURE_LIST + + @property + def name(self): + """Return the thermostats name.""" + return self._nhc2thermostat.name + + @property + def unique_id(self): + """Return the thermostats UUID.""" + return self._nhc2thermostat.uuid + + @property + def device_info(self): + """Return the device info.""" + return { + "identifiers": { + (DOMAIN, self.unique_id) + }, + "name": self.name, + "manufacturer": BRAND, + "model": CLIMATE, + "via_hub": (DOMAIN, self._nhc2thermostat.profile_creation_id), + } diff --git a/custom_components/nhc2/const.py b/custom_components/nhc2/const.py index cccd1a48..54b011e3 100644 --- a/custom_components/nhc2/const.py +++ b/custom_components/nhc2/const.py @@ -8,6 +8,7 @@ SWITCH = 'Switch' COVER = 'Cover' FAN = 'Fan' +CLIMATE = 'Thermostat' CONF_SWITCHES_AS_LIGHTS = 'switches_as_lights' DEFAULT_PORT = 8883 KEY_MANUAL = 'MANUAL_IP_HOST' @@ -15,4 +16,4 @@ ROLL_DOWN_SHUTTER = 'rolldownshutter' SUN_BLIND = 'sunblind' GATE = 'gate' -VENETIAN_BLIND = 'venetianblind' \ No newline at end of file +VENETIAN_BLIND = 'venetianblind' diff --git a/custom_components/nhc2/helpers.py b/custom_components/nhc2/helpers.py index b1fdf6db..2244217d 100644 --- a/custom_components/nhc2/helpers.py +++ b/custom_components/nhc2/helpers.py @@ -15,7 +15,7 @@ def nhc2_entity_processor(hass, """Loops the entities list and creates, updates or deletes HA entities.""" @callback def process_entities(entities): - _LOGGER.debug('Processing of entities started') + _LOGGER.debug('Processing of %s entities started', key) # Collect a list of active UUIDs active_uuids = list(map(lambda x: x.uuid, hass.data[key][config_entry.entry_id])) diff --git a/custom_components/nhc2/manifest.json b/custom_components/nhc2/manifest.json index 987a8a3d..8def2fa0 100644 --- a/custom_components/nhc2/manifest.json +++ b/custom_components/nhc2/manifest.json @@ -1,7 +1,7 @@ { "domain": "nhc2", "name": "Niko Home Control II", - "requirements": ["nhc2-coco==1.3.3"], + "requirements": ["nhc2-coco==1.4.1"], "config_flow": true, "issue_tracker": "https://github.com/filipvh/hass-nhc2/issues", "documentation": "https://github.com/filipvh/hass-nhc2/blob/master/README.md",