Python12 env rebuild

This commit is contained in:
Jean-Marc Collin
2024-02-25 17:18:14 +00:00
parent f7da58d841
commit 4478d65ad4
13 changed files with 81 additions and 42 deletions

View File

@@ -1,2 +1,2 @@
FROM mcr.microsoft.com/devcontainers/python:1-3.11 FROM mcr.microsoft.com/devcontainers/python:1-3.12
RUN apt update && apt install -y ffmpeg RUN apt update && apt install -y ffmpeg

View File

@@ -22,7 +22,8 @@
"extensions": [ "extensions": [
"ms-python.python", "ms-python.python",
"ms-python.pylint", "ms-python.pylint",
"ms-python.vscode-pylance", // already included into ms-python.python
// "ms-python.vscode-pylance",
"ms-python.isort", "ms-python.isort",
"ms-python.black-formatter", "ms-python.black-formatter",
"visualstudioexptteam.vscodeintellicode", "visualstudioexptteam.vscodeintellicode",

28
.vscode/launch.json vendored
View File

@@ -1,18 +1,14 @@
{ {
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Home Assistant (debug)", "name": "Home Assistant (debug)",
"type": "python", "type": "debugpy",
"request": "launch", "request": "launch",
"module": "homeassistant", "module": "homeassistant",
"justMyCode": false, "justMyCode": false,
"args": [ "args": ["--debug", "-c", "config"]
"--debug", }
"-c", ]
"config"
]
}
]
} }

View File

@@ -8,9 +8,7 @@
"files.associations": { "files.associations": {
"*.yaml": "home-assistant" "*.yaml": "home-assistant"
}, },
"python.testing.pytestArgs": [ "python.testing.pytestArgs": [],
"tests"
],
"python.testing.unittestEnabled": false, "python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true, "python.testing.pytestEnabled": true,
"python.analysis.extraPaths": [ "python.analysis.extraPaths": [
@@ -18,5 +16,6 @@
"/workspaces/versatile_thermostat/custom_components/versatile_thermostat", "/workspaces/versatile_thermostat/custom_components/versatile_thermostat",
"/home/vscode/.local/lib/python3.12/site-packages/homeassistant" "/home/vscode/.local/lib/python3.12/site-packages/homeassistant"
], ],
"python.experiments.optOutFrom": ["pythonTestAdapter"],
"python.formatting.provider": "none" "python.formatting.provider": "none"
} }

View File

@@ -158,7 +158,7 @@ Certains thermostat de type TRV sont réputés incompatibles avec le Versatile T
2. Les thermostats « Homematic » (et éventuellement Homematic IP) sont connus pour rencontrer des problèmes avec le Versatile Thermostat en raison des limitations du protocole RF sous-jacent. Ce problème se produit particulièrement lorsque vous essayez de contrôler plusieurs thermostats Homematic à la fois dans une seule instance de VTherm. Afin de réduire la charge du cycle de service, vous pouvez par ex. regroupez les thermostats avec des procédures spécifiques à Homematic (par exemple en utilisant un thermostat mural) et laissez Versatile Thermostat contrôler uniquement le thermostat mural directement. Une autre option consiste à contrôler un seul thermostat et à propager les changements de mode CVC et de température par un automatisme, 2. Les thermostats « Homematic » (et éventuellement Homematic IP) sont connus pour rencontrer des problèmes avec le Versatile Thermostat en raison des limitations du protocole RF sous-jacent. Ce problème se produit particulièrement lorsque vous essayez de contrôler plusieurs thermostats Homematic à la fois dans une seule instance de VTherm. Afin de réduire la charge du cycle de service, vous pouvez par ex. regroupez les thermostats avec des procédures spécifiques à Homematic (par exemple en utilisant un thermostat mural) et laissez Versatile Thermostat contrôler uniquement le thermostat mural directement. Une autre option consiste à contrôler un seul thermostat et à propager les changements de mode CVC et de température par un automatisme,
3. les thermostats de type Heatzy qui ne supportent pas les commandes de type set_temperature 3. les thermostats de type Heatzy qui ne supportent pas les commandes de type set_temperature
4. les thermostats de type Rointe ont tendance a se réveiller tout seul. Le reste fonctionne normalement. 4. les thermostats de type Rointe ont tendance a se réveiller tout seul. Le reste fonctionne normalement.
5. les TRV de type Aqara SRTS-A01 qui n'ont pas le retour d'état `hvac_action` permettant de savoir si elle chauffe ou pas. Donc les retours d'état sont faussés, le reste à l'air fonctionnel. 5. les TRV de type Aqara SRTS-A01 et MOES TV01-ZB qui n'ont pas le retour d'état `hvac_action` permettant de savoir si elle chauffe ou pas. Donc les retours d'état sont faussés, le reste à l'air fonctionnel.
# Pourquoi une nouvelle implémentation du thermostat ? # Pourquoi une nouvelle implémentation du thermostat ?

View File

@@ -158,7 +158,7 @@ Some TRV type thermostats are known to be incompatible with the Versatile Thermo
2. "Homematic" (and possible Homematic IP) thermostats are known to have problems with Versatile Thermostats because of limitations of the underlying RF protocol. This problem especially occurs when trying to control several Homematic thermostats at once in one Versatile Thermostat instance. In order to reduce duty cycle load, you may e.g. group thermostats with Homematic-specific procedures (e.g. using a wall thermostat) and let Versatile Thermostat only control the wall thermostat directly. Another option is to control only one thermostat and propagate the changes in HVAC mode and temperature by an automation. 2. "Homematic" (and possible Homematic IP) thermostats are known to have problems with Versatile Thermostats because of limitations of the underlying RF protocol. This problem especially occurs when trying to control several Homematic thermostats at once in one Versatile Thermostat instance. In order to reduce duty cycle load, you may e.g. group thermostats with Homematic-specific procedures (e.g. using a wall thermostat) and let Versatile Thermostat only control the wall thermostat directly. Another option is to control only one thermostat and propagate the changes in HVAC mode and temperature by an automation.
3. Thermostat of type Heatzy which doesn't supports the set_temperature command. 3. Thermostat of type Heatzy which doesn't supports the set_temperature command.
4. Thermostats of type Rointe tends to awake alone even if VTherm turns it off. Others functions works fine. 4. Thermostats of type Rointe tends to awake alone even if VTherm turns it off. Others functions works fine.
5. TRV of type Aqara SRTS-A01 which doesn't have the return state `hvac_action` allowing to know if it is heating or not. So return states are not available. Others features, seems to work normally. 5. TRV of type Aqara SRTS-A01 and MOES TV01-ZB which doesn't have the return state `hvac_action` allowing to know if it is heating or not. So return states are not available. Others features, seems to work normally.
# Why another thermostat implementation ? # Why another thermostat implementation ?

View File

@@ -40,13 +40,13 @@ HIDDEN_PRESETS = [PRESET_POWER, PRESET_SECURITY]
DOMAIN = "versatile_thermostat" DOMAIN = "versatile_thermostat"
# The order is important. # The order is important.
# NUMBER should be after CLIMATE,
PLATFORMS: list[Platform] = [ PLATFORMS: list[Platform] = [
Platform.SELECT, Platform.SELECT,
Platform.CLIMATE, Platform.CLIMATE,
Platform.SENSOR, Platform.SENSOR,
Platform.BINARY_SENSOR, # Number should be after CLIMATE
Platform.NUMBER, Platform.NUMBER,
Platform.BINARY_SENSOR,
] ]
CONF_HEATER = "heater_entity_id" CONF_HEATER = "heater_entity_id"

View File

@@ -1,4 +1,5 @@
""" The API of Versatile Thermostat""" """ The API of Versatile Thermostat"""
import logging import logging
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@@ -106,10 +107,21 @@ class VersatileThermostatAPI(dict):
): ):
"""register the two number entities needed for boiler activation""" """register the two number entities needed for boiler activation"""
self._threshold_number_entity = threshold_number_entity self._threshold_number_entity = threshold_number_entity
# If sensor and threshold number are initialized, reload the listener
if self._nb_active_number_entity and self._central_boiler_entity:
self._hass.async_add_job(self.reload_central_boiler_binary_listener)
def register_nb_device_active_boiler(self, nb_active_number_entity): def register_nb_device_active_boiler(self, nb_active_number_entity):
"""register the two number entities needed for boiler activation""" """register the two number entities needed for boiler activation"""
self._nb_active_number_entity = nb_active_number_entity self._nb_active_number_entity = nb_active_number_entity
if self._threshold_number_entity and self._central_boiler_entity:
self._hass.async_add_job(self.reload_central_boiler_binary_listener)
async def reload_central_boiler_binary_listener(self):
"""Reloads the BinarySensor entity which listen to the number of
active devices and the thresholds entities"""
if self._central_boiler_entity:
await self._central_boiler_entity.listen_nb_active_vtherm_entity()
async def reload_central_boiler_entities_list(self): async def reload_central_boiler_entities_list(self):
"""Reload the central boiler list of entities if a central boiler is used""" """Reload the central boiler list of entities if a central boiler is used"""

0
pyproject.toml Normal file
View File

View File

@@ -35,6 +35,19 @@ from .commons import (
FULL_CENTRAL_CONFIG_WITH_BOILER, FULL_CENTRAL_CONFIG_WITH_BOILER,
) )
# https://github.com/miketheman/pytest-socket/pull/275
from pytest_socket import socket_allow_hosts
# ...
# ...
def pytest_runtest_setup():
socket_allow_hosts(
allowed=["localhost", "127.0.0.1", "::1"], allow_unix_socket=True
)
pytest_plugins = "pytest_homeassistant_custom_component" # pylint: disable=invalid-name pytest_plugins = "pytest_homeassistant_custom_component" # pylint: disable=invalid-name

View File

@@ -156,23 +156,29 @@ async def test_update_central_boiler_state_simple(
await switch1.async_turn_on() await switch1.async_turn_on()
switch1.async_write_ha_state() switch1.async_write_ha_state()
# Wait for state event propagation # Wait for state event propagation
await asyncio.sleep(0.1) await asyncio.sleep(1)
assert entity.hvac_action == HVACAction.HEATING assert entity.hvac_action == HVACAction.HEATING
assert mock_service_call.call_count >= 1 assert mock_service_call.call_count == 2
# Sometimes this test fails # Sometimes this test fails
# mock_service_call.assert_has_calls( mock_service_call.assert_has_calls(
# [ [
# call.service_call( call.service_call(
# "switch", "switch",
# "turn_on", "turn_on",
# service_data={}, {"entity_id": "switch.switch1"},
# target={"entity_id": "switch.pompe_chaudiere"}, ),
# ), call(
# ] "switch",
# ) "turn_on",
service_data={},
target={"entity_id": "switch.pompe_chaudiere"},
),
],
any_order=True,
)
assert mock_send_event.call_count >= 1 assert mock_send_event.call_count >= 1
mock_send_event.assert_has_calls( mock_send_event.assert_has_calls(

View File

@@ -31,8 +31,8 @@ from .commons import * # pylint: disable=wildcard-import, unused-wildcard-impor
from .const import * # pylint: disable=wildcard-import, unused-wildcard-import from .const import * # pylint: disable=wildcard-import, unused-wildcard-import
# @pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
# @pytest.mark.parametrize("expected_lingering_timers", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_add_a_central_config(hass: HomeAssistant, skip_hass_states_is_state): async def test_add_a_central_config(hass: HomeAssistant, skip_hass_states_is_state):
"""Tests the clean_central_config_doubon of base_thermostat""" """Tests the clean_central_config_doubon of base_thermostat"""
central_config_entry = MockConfigEntry( central_config_entry = MockConfigEntry(
@@ -303,8 +303,8 @@ async def test_full_over_switch_wo_central_config(
entity.remove_thermostat() entity.remove_thermostat()
# @pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
# @pytest.mark.parametrize("expected_lingering_timers", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_full_over_switch_with_central_config( async def test_full_over_switch_with_central_config(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config hass: HomeAssistant, skip_hass_states_is_state, init_central_config
): ):

View File

@@ -170,6 +170,8 @@ async def test_config_with_central_mode_none(
assert entity.last_central_mode is None # cause no central config exists assert entity.last_central_mode is None # cause no central config exists
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_switch_change_central_mode_true( async def test_switch_change_central_mode_true(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config hass: HomeAssistant, skip_hass_states_is_state, init_central_config
): ):
@@ -310,6 +312,8 @@ async def test_switch_change_central_mode_true(
assert entity.preset_mode == PRESET_COMFORT assert entity.preset_mode == PRESET_COMFORT
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_switch_ac_change_central_mode_true( async def test_switch_ac_change_central_mode_true(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config hass: HomeAssistant, skip_hass_states_is_state, init_central_config
): ):
@@ -444,6 +448,8 @@ async def test_switch_ac_change_central_mode_true(
assert entity.preset_mode == PRESET_COMFORT assert entity.preset_mode == PRESET_COMFORT
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_climate_ac_change_central_mode_false( async def test_climate_ac_change_central_mode_false(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config hass: HomeAssistant, skip_hass_states_is_state, init_central_config
): ):
@@ -577,6 +583,8 @@ async def test_climate_ac_change_central_mode_false(
assert entity.preset_mode == PRESET_COMFORT assert entity.preset_mode == PRESET_COMFORT
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_climate_ac_only_change_central_mode_true( async def test_climate_ac_only_change_central_mode_true(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config hass: HomeAssistant, skip_hass_states_is_state, init_central_config
): ):
@@ -734,6 +742,8 @@ async def test_climate_ac_only_change_central_mode_true(
assert entity.preset_mode == PRESET_ECO assert entity.preset_mode == PRESET_ECO
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_switch_change_central_mode_true_with_window( async def test_switch_change_central_mode_true_with_window(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config hass: HomeAssistant, skip_hass_states_is_state, init_central_config
): ):
@@ -889,6 +899,8 @@ async def test_switch_change_central_mode_true_with_window(
assert entity.window_state is STATE_OFF assert entity.window_state is STATE_OFF
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_switch_change_central_mode_true_with_cool_only_and_window( async def test_switch_change_central_mode_true_with_cool_only_and_window(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config hass: HomeAssistant, skip_hass_states_is_state, init_central_config
): ):