""" Test the normal start of a Thermostat """ from datetime import timedelta, datetime from homeassistant.core import HomeAssistant from homeassistant.components.climate import HVACMode from homeassistant.components.sensor import ( SensorDeviceClass, SensorStateClass, ) from homeassistant.const import UnitOfTime, UnitOfPower, UnitOfEnergy, PERCENTAGE from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.versatile_thermostat.climate import VersatileThermostat from custom_components.versatile_thermostat.sensor import ( EnergySensor, MeanPowerSensor, OnPercentSensor, OnTimeSensor, OffTimeSensor, LastTemperatureSensor, LastExtTemperatureSensor, ) from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import @pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True]) async def test_sensors_over_switch( hass: HomeAssistant, skip_hass_states_is_state, skip_turn_on_off_heater, skip_send_event, ): """Test the sensors with a thermostat avec switch type""" entry = MockConfigEntry( domain=DOMAIN, title="TheOverSwitchMockName", unique_id="uniqueId", data={ CONF_NAME: "TheOverSwitchMockName", CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH, CONF_TEMP_SENSOR: "sensor.mock_temp_sensor", CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor", CONF_CYCLE_MIN: 5, CONF_TEMP_MIN: 15, CONF_TEMP_MAX: 30, "eco_temp": 17, "comfort_temp": 18, "boost_temp": 19, CONF_USE_WINDOW_FEATURE: False, CONF_USE_MOTION_FEATURE: False, CONF_USE_POWER_FEATURE: False, CONF_USE_PRESENCE_FEATURE: False, CONF_HEATER: "switch.mock_switch", CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, CONF_SECURITY_DELAY_MIN: 5, CONF_SECURITY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 200, }, ) entity: VersatileThermostat = await create_thermostat( hass, entry, "climate.theoverswitchmockname" ) assert entity energy_sensor: EnergySensor = search_entity( hass, "sensor.theoverswitchmockname_energy", "sensor" ) assert energy_sensor mean_power_sensor: MeanPowerSensor = search_entity( hass, "sensor.theoverswitchmockname_mean_power_cycle", "sensor" ) assert mean_power_sensor on_percent_sensor: OnPercentSensor = search_entity( hass, "sensor.theoverswitchmockname_power_percent", "sensor" ) assert on_percent_sensor on_time_sensor: OnTimeSensor = search_entity( hass, "sensor.theoverswitchmockname_on_time", "sensor" ) assert on_time_sensor off_time_sensor: OffTimeSensor = search_entity( hass, "sensor.theoverswitchmockname_off_time", "sensor" ) assert off_time_sensor last_temperature_sensor: LastTemperatureSensor = search_entity( hass, "sensor.theoverswitchmockname_last_temperature_date", "sensor" ) assert last_temperature_sensor last_ext_temperature_sensor: LastExtTemperatureSensor = search_entity( hass, "sensor.theoverswitchmockname_last_external_temperature_date", "sensor" ) assert last_ext_temperature_sensor # Simulate the event reception await energy_sensor.async_my_climate_changed() assert energy_sensor.state == 0.0 await mean_power_sensor.async_my_climate_changed() assert mean_power_sensor.state == 0.0 await on_percent_sensor.async_my_climate_changed() assert on_percent_sensor.state == 0.0 await on_time_sensor.async_my_climate_changed() assert on_time_sensor.state == 0.0 await off_time_sensor.async_my_climate_changed() assert off_time_sensor.state == 300.0 await last_temperature_sensor.async_my_climate_changed() assert last_temperature_sensor.state is not None await last_ext_temperature_sensor.async_my_climate_changed() assert last_ext_temperature_sensor.state is not None last_temp_date = last_temperature_sensor.state tz = get_tz(hass) # pylint: disable=invalid-name now: datetime = datetime.now(tz=tz) event_timestamp = now - timedelta(minutes=1) # Start the heater to get some values await entity.async_set_preset_mode(PRESET_COMFORT) await entity.async_set_hvac_mode(HVACMode.HEAT) await send_temperature_change_event(entity, 15, event_timestamp) await send_ext_temperature_change_event(entity, 5, event_timestamp) entity.incremente_energy() await energy_sensor.async_my_climate_changed() assert energy_sensor.state == 16.667 assert energy_sensor.device_class == SensorDeviceClass.ENERGY assert energy_sensor.state_class == SensorStateClass.TOTAL_INCREASING # because device_power is 200 assert energy_sensor.unit_of_measurement == UnitOfEnergy.WATT_HOUR await mean_power_sensor.async_my_climate_changed() assert mean_power_sensor.state == 200.0 assert mean_power_sensor.device_class == SensorDeviceClass.POWER assert mean_power_sensor.state_class == SensorStateClass.MEASUREMENT # because device_power is 200 assert mean_power_sensor.unit_of_measurement == UnitOfPower.WATT await on_percent_sensor.async_my_climate_changed() assert on_percent_sensor.state == 100.0 assert on_percent_sensor.unit_of_measurement == PERCENTAGE await on_time_sensor.async_my_climate_changed() assert on_time_sensor.state == 300.0 assert on_time_sensor.device_class == SensorDeviceClass.DURATION assert on_time_sensor.state_class == SensorStateClass.MEASUREMENT assert on_time_sensor.unit_of_measurement == UnitOfTime.SECONDS await off_time_sensor.async_my_climate_changed() assert off_time_sensor.state == 0.0 assert off_time_sensor.device_class == SensorDeviceClass.DURATION assert off_time_sensor.state_class == SensorStateClass.MEASUREMENT assert off_time_sensor.unit_of_measurement == UnitOfTime.SECONDS await last_temperature_sensor.async_my_climate_changed() assert ( last_temperature_sensor.state is not None and last_temperature_sensor.state != last_temp_date ) assert last_temperature_sensor.device_class == SensorDeviceClass.TIMESTAMP await last_ext_temperature_sensor.async_my_climate_changed() assert ( last_ext_temperature_sensor.state is not None and last_ext_temperature_sensor.state != last_temp_date ) assert last_ext_temperature_sensor.device_class == SensorDeviceClass.TIMESTAMP cancel_switchs_cycles(entity) @pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True]) async def test_sensors_over_climate( hass: HomeAssistant, skip_hass_states_is_state, skip_turn_on_off_heater, skip_send_event, ): """Test the sensors with thermostat over climate type""" the_mock_underlying = MagicMockClimate() with patch( "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate", return_value=the_mock_underlying, ): entry = MockConfigEntry( domain=DOMAIN, title="TheOverClimateMockName", unique_id="uniqueId", data={ CONF_NAME: "TheOverClimateMockName", CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE, CONF_TEMP_SENSOR: "sensor.mock_temp_sensor", CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor", CONF_CYCLE_MIN: 5, CONF_TEMP_MIN: 15, CONF_TEMP_MAX: 30, "eco_temp": 17, "comfort_temp": 18, "boost_temp": 19, CONF_USE_WINDOW_FEATURE: False, CONF_USE_MOTION_FEATURE: False, CONF_USE_POWER_FEATURE: True, CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, CONF_SECURITY_DELAY_MIN: 5, CONF_SECURITY_MIN_ON_PERCENT: 0.3, CONF_POWER_SENSOR: "sensor.mock_power_sensor", CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor", CONF_DEVICE_POWER: 1.5, CONF_PRESET_POWER: 12, }, ) entity: VersatileThermostat = await create_thermostat( hass, entry, "climate.theoverclimatemockname" ) assert entity assert entity.is_over_climate energy_sensor: EnergySensor = search_entity( hass, "sensor.theoverclimatemockname_energy", "sensor" ) assert energy_sensor last_temperature_sensor: LastTemperatureSensor = search_entity( hass, "sensor.theoverclimatemockname_last_temperature_date", "sensor" ) assert last_temperature_sensor last_ext_temperature_sensor: LastExtTemperatureSensor = search_entity( hass, "sensor.theoverclimatemockname_last_external_temperature_date", "sensor" ) assert last_ext_temperature_sensor # Simulate the event reception await energy_sensor.async_my_climate_changed() assert energy_sensor.state == 0.0 await last_temperature_sensor.async_my_climate_changed() assert last_temperature_sensor.state is not None await last_ext_temperature_sensor.async_my_climate_changed() assert last_ext_temperature_sensor.state is not None last_temp_date = last_temperature_sensor.state tz = get_tz(hass) # pylint: disable=invalid-name now: datetime = datetime.now(tz=tz) event_timestamp = now - timedelta(minutes=1) # Start the heater to get some values await entity.async_set_preset_mode(PRESET_COMFORT) await entity.async_set_hvac_mode(HVACMode.HEAT) await send_temperature_change_event(entity, 15, event_timestamp) await send_ext_temperature_change_event(entity, 5, event_timestamp) # to add energy we must have HVACAction underlying climate event # Send a climate_change event with HVACAction=HEATING event_timestamp = now - timedelta(minutes=60) await send_climate_change_event( entity, new_hvac_mode=HVACMode.HEAT, old_hvac_mode=HVACMode.HEAT, new_hvac_action=HVACAction.HEATING, old_hvac_action=HVACAction.OFF, date=event_timestamp, ) # Send a climate_change event with HVACAction=IDLE (end of heating) await send_climate_change_event( entity, new_hvac_mode=HVACMode.HEAT, old_hvac_mode=HVACMode.HEAT, new_hvac_action=HVACAction.IDLE, old_hvac_action=HVACAction.HEATING, date=now, ) # 60 minutes heating with 1.5 kW heating -> 1.5 kWh await energy_sensor.async_my_climate_changed() assert energy_sensor.state == 1.5 assert energy_sensor.device_class == SensorDeviceClass.ENERGY assert energy_sensor.state_class == SensorStateClass.TOTAL_INCREASING # because device_power is 1.5 kW assert energy_sensor.unit_of_measurement == UnitOfEnergy.KILO_WATT_HOUR entity.incremente_energy() await energy_sensor.async_my_climate_changed() assert energy_sensor.state == 3.0 await last_temperature_sensor.async_my_climate_changed() assert ( last_temperature_sensor.state is not None and last_temperature_sensor.state != last_temp_date ) assert last_temperature_sensor.device_class == SensorDeviceClass.TIMESTAMP await last_ext_temperature_sensor.async_my_climate_changed() assert ( last_ext_temperature_sensor.state is not None and last_ext_temperature_sensor.state != last_temp_date ) assert last_ext_temperature_sensor.device_class == SensorDeviceClass.TIMESTAMP @pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True]) async def test_sensors_over_climate_minimal( hass: HomeAssistant, skip_hass_states_is_state, skip_turn_on_off_heater, skip_send_event, ): """Test the sensors with thermostat over climate type""" the_mock_underlying = MagicMockClimate() with patch( "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate", return_value=the_mock_underlying, ): entry = MockConfigEntry( domain=DOMAIN, title="TheOverClimateMockName", unique_id="uniqueId", data={ CONF_NAME: "TheOverClimateMockName", CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE, CONF_TEMP_SENSOR: "sensor.mock_temp_sensor", CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor", CONF_CYCLE_MIN: 5, CONF_TEMP_MIN: 15, CONF_TEMP_MAX: 30, "eco_temp": 17, "comfort_temp": 18, "boost_temp": 19, CONF_USE_WINDOW_FEATURE: False, CONF_USE_MOTION_FEATURE: False, CONF_USE_POWER_FEATURE: False, CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, CONF_SECURITY_DELAY_MIN: 5, CONF_SECURITY_MIN_ON_PERCENT: 0.3, }, ) entity: VersatileThermostat = await create_thermostat( hass, entry, "climate.theoverclimatemockname" ) assert entity assert entity.is_over_climate energy_sensor: EnergySensor = search_entity( hass, "sensor.theoverclimatemockname_energy", "sensor" ) assert energy_sensor is None last_temperature_sensor: LastTemperatureSensor = search_entity( hass, "sensor.theoverclimatemockname_last_temperature_date", "sensor" ) assert last_temperature_sensor last_ext_temperature_sensor: LastExtTemperatureSensor = search_entity( hass, "sensor.theoverclimatemockname_last_external_temperature_date", "sensor" ) assert last_ext_temperature_sensor