Compare commits

..

9 Commits

Author SHA1 Message Date
Jean-Marc Collin
af51ef62e0 Issue #99 : over climate VTherm a regulated by the device itself and should not goes into security 2023-08-30 09:06:26 +02:00
Jean-Marc Collin
b38fbd9d78 Issue #99 - security mode toggling 100 times within 2 minutes 2023-08-27 18:06:43 +02:00
Jean-Marc Collin
6e8e72e343 FIX Service name Github error 2023-08-16 22:30:46 +02:00
Jean-Marc Collin
2bebe3e210 Issue #95 - the integration would switch ac on and off rapidly and lock up home assistant if outside temp is NaN 2023-08-05 20:02:54 +02:00
Jean-Marc Collin
aa3b87762d FIX AC climate stops even if already stopped 2023-07-30 21:25:20 +02:00
Jean-Marc Collin
f4cabbf2c0 FIX unit tests 2023-07-30 18:09:42 +02:00
Jean-Marc Collin
24b59e545b FIX cancel_timer await 2023-07-29 11:22:50 +02:00
Jean-Marc Collin
5997a26c73 FIX #90 WarCOzes remark 2023-07-23 09:08:56 +02:00
Jean-Marc Collin
fe4b9ced81 Documentation 2023-07-22 17:23:42 +02:00
11 changed files with 343 additions and 326 deletions

View File

@@ -141,9 +141,9 @@ template:
device_class: energy device_class: energy
state_class: total_increasing state_class: total_increasing
state: > state: >
{% set energy = state_attr('climate.thermostat_switch_1', 'total_energy') %} {% set energy = state_attr('climate.thermostat_switch_1', 'total_energy') | float(default=-1) %}
{% if energy == 'unavailable' or energy is none%}unavailable{% else %} {% if energy < 0 %}{{none}}{% else %}
{{ ((energy | float) / 1.0) | round(2, default=0) }} {{ energy | round(2, default=0) }}
{% endif %} {% endif %}
- name: "Total énergie climate 2" - name: "Total énergie climate 2"
unique_id: total_energie_climate2 unique_id: total_energie_climate2
@@ -151,9 +151,9 @@ template:
device_class: energy device_class: energy
state_class: total_increasing state_class: total_increasing
state: > state: >
{% set energy = state_attr('climate.thermostat_climate_2', 'total_energy') %} {% set energy = state_attr('climate.thermostat_climate_2', 'total_energy') | float(default=-1) %}
{% if energy == 'unavailable' or energy is none%}unavailable{% else %} {% if energy < 0 %}{{none}}{% else %}
{{ ((energy | float) / 1.0) | round(2, default=0) }} {{ energy | round(2, default=0) }}
{% endif %} {% endif %}
- name: "Total énergie chambre" - name: "Total énergie chambre"
unique_id: total_energie_chambre unique_id: total_energie_chambre
@@ -161,9 +161,9 @@ template:
device_class: energy device_class: energy
state_class: total_increasing state_class: total_increasing
state: > state: >
{% set energy = state_attr('climate.thermostat_chambre', 'total_energy') %} {% set energy = state_attr('climate.thermostat_chambre', 'total_energy') | float(default=-1) %}
{% if energy == 'unavailable' or energy is none%}unavailable{% else %} {% if energy < 0 %}{{none}}{% else %}
{{ ((energy | float) / 1.0) | round(2, default=0) }} {{ energy | round(2, default=0) }}
{% endif %} {% endif %}
switch: switch:

View File

@@ -8,7 +8,7 @@
> ![Tip](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/tips.png?raw=true) Cette intégration de thermostat vise à simplifier considérablement vos automatisations autour de la gestion du chauffage. Parce que tous les événements autour du chauffage classiques sont gérés nativement par le thermostat (personne à la maison ?, activité détectée dans une pièce ?, fenêtre ouverte ?, délestage de courant ?), vous n'avez pas à vous encombrer de scripts et d'automatismes compliqués pour gérer vos climats. ;-). > ![Tip](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/tips.png?raw=true) Cette intégration de thermostat vise à simplifier considérablement vos automatisations autour de la gestion du chauffage. Parce que tous les événements autour du chauffage classiques sont gérés nativement par le thermostat (personne à la maison ?, activité détectée dans une pièce ?, fenêtre ouverte ?, délestage de courant ?), vous n'avez pas à vous encombrer de scripts et d'automatismes compliqués pour gérer vos climats. ;-).
- [Merci pour la bière buymecoffee: https://www.buymeacoffee.com/jmcollin78](#merci-pour-la-bière-buymecoffee-httpswwwbuymeacoffeecomjmcollin78) - [Merci pour la bière buymecoffee](#merci-pour-la-bière-buymecoffee)
- [Quand l'utiliser et ne pas l'utiliser](#quand-lutiliser-et-ne-pas-lutiliser) - [Quand l'utiliser et ne pas l'utiliser](#quand-lutiliser-et-ne-pas-lutiliser)
- [Pourquoi une nouvelle implémentation du thermostat ?](#pourquoi-une-nouvelle-implémentation-du-thermostat-) - [Pourquoi une nouvelle implémentation du thermostat ?](#pourquoi-une-nouvelle-implémentation-du-thermostat-)
- [Comment installer cet incroyable Thermostat Versatile ?](#comment-installer-cet-incroyable-thermostat-versatile-) - [Comment installer cet incroyable Thermostat Versatile ?](#comment-installer-cet-incroyable-thermostat-versatile-)
@@ -54,6 +54,7 @@ Ce composant personnalisé pour Home Assistant est une mise à niveau et est une
> ![Nouveau](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/new-icon.png?raw=true) _*Nouveautés*_ > ![Nouveau](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/new-icon.png?raw=true) _*Nouveautés*_
> > * **Release 3.3**: ajout du mode Air Conditionné (AC). Cette fonction vous permet d'utiliser le mode AC de votre thermostat sous-jacent. Pour l'utiliser, vous devez cocher l'option "Uitliser le mode AC" et définir les valeurs de température pour les presets et pour les presets en cas d'absence
> * **Release 3.2** : ajout de la possibilité de commander plusieurs switch à partir du même thermostat. Dans ce mode, les switchs sont déclenchés avec un délai pour minimiser la puissance nécessaire à un instant (on minimise les périodes de recouvrement). Voir [Configuration](#sélectionnez-des-entités-pilotées) > * **Release 3.2** : ajout de la possibilité de commander plusieurs switch à partir du même thermostat. Dans ce mode, les switchs sont déclenchés avec un délai pour minimiser la puissance nécessaire à un instant (on minimise les périodes de recouvrement). Voir [Configuration](#sélectionnez-des-entités-pilotées)
> * **Release 3.1** : ajout d'une détection de fenêtres/portes ouvertes par chute de température. Cette nouvelle fonction permet de stopper automatiquement un radiateur lorsque la température chute brutalement. Voir [Le mode auto](#le-mode-auto) > * **Release 3.1** : ajout d'une détection de fenêtres/portes ouvertes par chute de température. Cette nouvelle fonction permet de stopper automatiquement un radiateur lorsque la température chute brutalement. Voir [Le mode auto](#le-mode-auto)
> * **Release majeure 3.0** : ajout d'un équipement thermostat et de capteurs (binaires et non binaires) associés. Beaucoup plus proche de la philosphie Home Assistant, vous avez maintenant un accès direct à l'énergie consommée par le radiateur piloté par le thermostat et à plein d'autres capteurs qui seront utiles dans vos automatisations et dashboard. > * **Release majeure 3.0** : ajout d'un équipement thermostat et de capteurs (binaires et non binaires) associés. Beaucoup plus proche de la philosphie Home Assistant, vous avez maintenant un accès direct à l'énergie consommée par le radiateur piloté par le thermostat et à plein d'autres capteurs qui seront utiles dans vos automatisations et dashboard.

View File

@@ -53,7 +53,8 @@
This custom component for Home Assistant is an upgrade and is a complete rewrite of the component "Awesome thermostat" (see [Github](https://github.com/dadge/awesome_thermostat)) with addition of features. This custom component for Home Assistant is an upgrade and is a complete rewrite of the component "Awesome thermostat" (see [Github](https://github.com/dadge/awesome_thermostat)) with addition of features.
>![New](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/new-icon.png?raw=true) _*News*_ >![New](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/new-icon.png?raw=true) _*News*_
> * **Release 3.2**: added the ability to control multiple switches from the same thermostat. In this mode, the switches are triggered with a delay to minimize the power required at one time (we minimize the recovery periods). See [Configuration](#select-the-driven-entity) > * **Release 3.3**: add the Air Conditionned mode (AC). This feature allow to use the eventual AC mode of your underlying climate entity. You have to check the "Use AC mode" checkbox in configuration and give preset temperature value for AC mode and AC mode when absent if absence is configured
> * **Release 3.2**: add the ability to control multiple switches from the same thermostat. In this mode, the switches are triggered with a delay to minimize the power required at one time (we minimize the recovery periods). See [Configuration](#select-the-driven-entity)
> * **Release 3.1**: added detection of open windows/doors by temperature drop. This new function makes it possible to automatically stop a radiator when the temperature drops suddenly. See [Auto mode](#auto-mode) > * **Release 3.1**: added detection of open windows/doors by temperature drop. This new function makes it possible to automatically stop a radiator when the temperature drops suddenly. See [Auto mode](#auto-mode)
> * **Major release 3.0**: addition of thermostat equipment and associated sensors (binary and non-binary). Much closer to the Home Assistant philosophy, you now have direct access to the energy consumed by the radiator controlled by the thermostat and many other sensors that will be useful in your automations and dashboard. > * **Major release 3.0**: addition of thermostat equipment and associated sensors (binary and non-binary). Much closer to the Home Assistant philosophy, you now have direct access to the energy consumed by the radiator controlled by the thermostat and many other sensors that will be useful in your automations and dashboard.
> * **release 2.3**: addition of the power and energy measurement of the radiator controlled by the thermostat. > * **release 2.3**: addition of the power and energy measurement of the radiator controlled by the thermostat.

View File

@@ -1215,6 +1215,10 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
await under.set_hvac_mode(hvac_mode) or need_control_heating await under.set_hvac_mode(hvac_mode) or need_control_heating
) )
# If AC is on maybe we have to change the temperature in force mode
if self._ac_mode:
await self._async_set_preset_mode_internal(self._attr_preset_mode, True)
if need_control_heating and sub_need_control_heating: if need_control_heating and sub_need_control_heating:
await self._async_control_heating(force=True) await self._async_control_heating(force=True)
@@ -1578,6 +1582,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
if not new_state: if not new_state:
return return
new_hvac_mode = new_state.state
old_state = event.data.get("old_state") old_state = event.data.get("old_state")
old_hvac_action = ( old_hvac_action = (
old_state.attributes.get("hvac_action") old_state.attributes.get("hvac_action")
@@ -1590,16 +1596,21 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
else None else None
) )
# Issue 99 - some AC turn hvac_mode=cool and hvac_action=idle when sending a HVACMode_OFF command
if self._hvac_mode == HVACMode.OFF and new_hvac_action == HVACAction.IDLE:
_LOGGER.debug("The underlying switch to idle instead of OFF. We will consider it as OFF")
new_hvac_mode = HVACMode.OFF
_LOGGER.info( _LOGGER.info(
"%s - Underlying climate changed. Event.new_state is %s, current_hvac_mode=%s, new_hvac_action=%s, old_hvac_action=%s", "%s - Underlying climate changed. Event.new_hvac_mode is %s, current_hvac_mode=%s, new_hvac_action=%s, old_hvac_action=%s",
self, self,
new_state, new_hvac_mode,
self._hvac_mode, self._hvac_mode,
new_hvac_action, new_hvac_action,
old_hvac_action, old_hvac_action,
) )
if new_state.state in [ if new_hvac_mode in [
HVACMode.OFF, HVACMode.OFF,
HVACMode.HEAT, HVACMode.HEAT,
HVACMode.COOL, HVACMode.COOL,
@@ -1607,8 +1618,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
HVACMode.DRY, HVACMode.DRY,
HVACMode.AUTO, HVACMode.AUTO,
HVACMode.FAN_ONLY, HVACMode.FAN_ONLY,
None
]: ]:
self._hvac_mode = new_state.state self._hvac_mode = new_hvac_mode
# Interpretation of hvac # Interpretation of hvac
HVAC_ACTION_ON = [ # pylint: disable=invalid-name HVAC_ACTION_ON = [ # pylint: disable=invalid-name
@@ -2094,9 +2106,18 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
switch_cond, switch_cond,
) )
ret = False # Issue 99 - a climate is regulated by the device itself and not by VTherm. So a VTherm should never be in security !
if mode_cond and temp_cond and climate_cond: shouldClimateBeInSecurity = False # temp_cond and climate_cond
if not self._security_state: shouldSwitchBeInSecurity = temp_cond and switch_cond
shouldBeInSecurity = shouldClimateBeInSecurity or shouldSwitchBeInSecurity
shouldStartSecurity = mode_cond and not self._security_state and shouldBeInSecurity
# attr_preset_mode is not necessary normaly. It is just here to be sure
shouldStopSecurity = self._security_state and not shouldBeInSecurity and self._attr_preset_mode == PRESET_SECURITY
# Logging and event
if shouldStartSecurity:
if shouldClimateBeInSecurity:
_LOGGER.warning( _LOGGER.warning(
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Set it into security mode", "%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Set it into security mode",
self, self,
@@ -2105,10 +2126,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
delta_ext_temp, delta_ext_temp,
self.hvac_action, self.hvac_action,
) )
ret = True elif shouldSwitchBeInSecurity:
if mode_cond and temp_cond and switch_cond:
if not self._security_state:
_LOGGER.warning( _LOGGER.warning(
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f) is over defined value (%.2f). Set it into security mode", "%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f) is over defined value (%.2f). Set it into security mode",
self, self,
@@ -2118,9 +2136,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._prop_algorithm.on_percent, self._prop_algorithm.on_percent,
self._security_min_on_percent, self._security_min_on_percent,
) )
ret = True
if mode_cond and temp_cond and not self._security_state:
self.send_event( self.send_event(
EventType.TEMPERATURE_EVENT, EventType.TEMPERATURE_EVENT,
{ {
@@ -2136,8 +2152,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
}, },
) )
if not self._security_state and ret: if shouldStartSecurity:
self._security_state = ret self._security_state = True
self.save_hvac_mode() self.save_hvac_mode()
self.save_preset_mode() self.save_preset_mode()
await self._async_set_preset_mode_internal(PRESET_SECURITY) await self._async_set_preset_mode_internal(PRESET_SECURITY)
@@ -2163,18 +2179,14 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
}, },
) )
if ( if shouldStopSecurity:
self._security_state
and self._attr_preset_mode == PRESET_SECURITY
and not ret
):
_LOGGER.warning( _LOGGER.warning(
"%s - End of security mode. restoring hvac_mode to %s and preset_mode to %s", "%s - End of security mode. restoring hvac_mode to %s and preset_mode to %s",
self, self,
self._saved_hvac_mode, self._saved_hvac_mode,
self._saved_preset_mode, self._saved_preset_mode,
) )
self._security_state = ret self._security_state = False
# Restore hvac_mode if previously saved # Restore hvac_mode if previously saved
if self._is_over_climate or self._security_default_on_percent <= 0.0: if self._is_over_climate or self._security_default_on_percent <= 0.0:
await self.restore_hvac_mode(False) await self.restore_hvac_mode(False)
@@ -2197,7 +2209,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
}, },
) )
return ret return shouldBeInSecurity
async def _async_control_heating(self, force=False, _=None): async def _async_control_heating(self, force=False, _=None):
"""The main function used to run the calculation at each cycle""" """The main function used to run the calculation at each cycle"""

View File

@@ -550,7 +550,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
"""Handle the presence management flow steps""" """Handle the presence management flow steps"""
_LOGGER.debug("Into ConfigFlow.async_step_presence user_input=%s", user_input) _LOGGER.debug("Into ConfigFlow.async_step_presence user_input=%s", user_input)
if self._infos[CONF_AC_MODE]: if self._infos.get(CONF_AC_MODE) == True:
schema = self.STEP_PRESENCE_WITH_AC_DATA_SCHEMA schema = self.STEP_PRESENCE_WITH_AC_DATA_SCHEMA
else: else:
schema = self.STEP_PRESENCE_DATA_SCHEMA schema = self.STEP_PRESENCE_DATA_SCHEMA
@@ -689,7 +689,7 @@ class VersatileThermostatOptionsFlowHandler(
elif self._infos[CONF_USE_PRESENCE_FEATURE]: elif self._infos[CONF_USE_PRESENCE_FEATURE]:
next_step = self.async_step_presence next_step = self.async_step_presence
if self._infos[CONF_AC_MODE]: if self._infos.get(CONF_AC_MODE) == True:
schema = self.STEP_PRESETS_WITH_AC_DATA_SCHEMA schema = self.STEP_PRESETS_WITH_AC_DATA_SCHEMA
else: else:
schema = self.STEP_PRESETS_DATA_SCHEMA schema = self.STEP_PRESETS_DATA_SCHEMA
@@ -752,7 +752,7 @@ class VersatileThermostatOptionsFlowHandler(
"Into OptionsFlowHandler.async_step_presence user_input=%s", user_input "Into OptionsFlowHandler.async_step_presence user_input=%s", user_input
) )
if self._infos[CONF_AC_MODE]: if self._infos.get(CONF_AC_MODE) == True:
schema = self.STEP_PRESENCE_WITH_AC_DATA_SCHEMA schema = self.STEP_PRESENCE_WITH_AC_DATA_SCHEMA
else: else:
schema = self.STEP_PRESENCE_DATA_SCHEMA schema = self.STEP_PRESENCE_DATA_SCHEMA

View File

@@ -1,2 +1,2 @@
homeassistant homeassistant==2023.8.3
ffmpeg ffmpeg

View File

@@ -1,4 +1,5 @@
reload: reload:
name: Reload
description: Reload all Versatile Thermostat entities. description: Reload all Versatile Thermostat entities.
set_presence: set_presence:

View File

@@ -15,6 +15,7 @@ from custom_components.versatile_thermostat.const import (
CONF_THERMOSTAT_CLIMATE, CONF_THERMOSTAT_CLIMATE,
CONF_THERMOSTAT_SWITCH, CONF_THERMOSTAT_SWITCH,
CONF_THERMOSTAT_TYPE, CONF_THERMOSTAT_TYPE,
CONF_AC_MODE,
CONF_TEMP_SENSOR, CONF_TEMP_SENSOR,
CONF_EXTERNAL_TEMP_SENSOR, CONF_EXTERNAL_TEMP_SENSOR,
CONF_CYCLE_MIN, CONF_CYCLE_MIN,
@@ -112,6 +113,7 @@ MOCK_TH_OVER_SWITCH_TPI_CONFIG = {
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = { MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
CONF_CLIMATE: "climate.mock_climate", CONF_CLIMATE: "climate.mock_climate",
CONF_AC_MODE: False,
} }
MOCK_PRESETS_CONFIG = { MOCK_PRESETS_CONFIG = {

View File

@@ -94,7 +94,7 @@ async def test_window_management_time_not_enough(
await try_window_condition(None) await try_window_condition(None)
assert entity.window_state == STATE_OFF assert entity.window_state == STATE_OFF
await entity.remove_thermostat() entity.remove_thermostat()
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
@@ -234,7 +234,7 @@ async def test_window_management_time_enough(
assert entity.preset_mode is PRESET_BOOST assert entity.preset_mode is PRESET_BOOST
# Clean the entity # Clean the entity
await entity.remove_thermostat() entity.remove_thermostat()
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
@@ -418,7 +418,7 @@ async def test_window_auto_fast(hass: HomeAssistant, skip_hass_states_is_state):
assert entity.hvac_mode is HVACMode.HEAT assert entity.hvac_mode is HVACMode.HEAT
# Clean the entity # Clean the entity
await entity.remove_thermostat() entity.remove_thermostat()
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
@@ -561,7 +561,7 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
assert entity.preset_mode is PRESET_BOOST assert entity.preset_mode is PRESET_BOOST
# Clean the entity # Clean the entity
await entity.remove_thermostat() entity.remove_thermostat()
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
@@ -670,4 +670,4 @@ async def test_window_auto_no_on_percent(
assert entity.hvac_mode is HVACMode.HEAT assert entity.hvac_mode is HVACMode.HEAT
# Clean the entity # Clean the entity
await entity.remove_thermostat() entity.remove_thermostat()

View File

@@ -180,7 +180,7 @@ class UnderlyingSwitch(UnderlyingEntity):
if hvac_mode == HVACMode.OFF: if hvac_mode == HVACMode.OFF:
if self.is_device_active: if self.is_device_active:
await self.turn_off() await self.turn_off()
await self._cancel_cycle() self._cancel_cycle()
if self._hvac_mode != hvac_mode: if self._hvac_mode != hvac_mode:
self._hvac_mode = hvac_mode self._hvac_mode = hvac_mode
@@ -275,7 +275,7 @@ class UnderlyingSwitch(UnderlyingEntity):
self._on_time_sec, self._on_time_sec,
) )
await self._cancel_cycle() self._cancel_cycle()
if self._hvac_mode == HVACMode.OFF: if self._hvac_mode == HVACMode.OFF:
_LOGGER.debug("%s - End of cycle (HVAC_MODE_OFF - 2)", self) _LOGGER.debug("%s - End of cycle (HVAC_MODE_OFF - 2)", self)
@@ -327,7 +327,7 @@ class UnderlyingSwitch(UnderlyingEntity):
self._should_relaunch_control_heating, self._should_relaunch_control_heating,
self._off_time_sec, self._off_time_sec,
) )
await self._cancel_cycle() self._cancel_cycle()
if self._hvac_mode == HVACMode.OFF: if self._hvac_mode == HVACMode.OFF:
_LOGGER.debug("%s - End of cycle (HVAC_MODE_OFF - 2)", self) _LOGGER.debug("%s - End of cycle (HVAC_MODE_OFF - 2)", self)
@@ -446,7 +446,7 @@ class UnderlyingClimate(UnderlyingEntity):
def is_device_active(self): def is_device_active(self):
"""If the toggleable device is currently active.""" """If the toggleable device is currently active."""
if self.is_initialized: if self.is_initialized:
return self._underlying_climate.hvac_action not in [ return self._underlying_climate.hvac_mode != HVACMode.OFF and self._underlying_climate.hvac_action not in [
HVACAction.IDLE, HVACAction.IDLE,
HVACAction.OFF, HVACAction.OFF,
] ]

View File

@@ -3,5 +3,5 @@
"content_in_root": false, "content_in_root": false,
"render_readme": true, "render_readme": true,
"hide_default_branch": false, "hide_default_branch": false,
"homeassistant": "2022.2.0" "homeassistant": "2023.7.3"
} }