Skip to content

Commit f3ed2b6

Browse files
Address niobos review feedback
- Add super().__init__() to ConfigFlow and OptionsFlowHandler - Extract duplicate experimental schema into _get_experimental_schema() helper - Make set_remote_temp_mode() async and move set_current_temperature(None) call into it when disabling remote mode - Remove unused type: ignore comments in climate.py - Update tests for async set_remote_temp_mode
1 parent 35535c0 commit f3ed2b6

6 files changed

Lines changed: 63 additions & 58 deletions

File tree

custom_components/mitsubishi/climate.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ class MitsubishiClimate(MitsubishiEntity, ClimateEntity):
118118

119119
_attr_supported_features = (
120120
0
121-
| ClimateEntityFeature.TURN_ON # type: ignore[attr-defined]
122-
| ClimateEntityFeature.TURN_OFF # type: ignore[attr-defined]
121+
| ClimateEntityFeature.TURN_ON
122+
| ClimateEntityFeature.TURN_OFF
123123
| ClimateEntityFeature.TARGET_TEMPERATURE
124124
| ClimateEntityFeature.FAN_MODE
125125
| ClimateEntityFeature.SWING_MODE
126-
| ClimateEntityFeature.SWING_HORIZONTAL_MODE # type: ignore[attr-defined]
126+
| ClimateEntityFeature.SWING_HORIZONTAL_MODE
127127
)
128128

129129
def __init__(

custom_components/mitsubishi/config_flow.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,30 @@
2929
DOMAIN,
3030
)
3131

32+
33+
def _get_experimental_schema(suggested_value: str | None = None) -> vol.Schema:
34+
"""Get the schema for experimental features configuration."""
35+
entity_selector = selector.EntitySelector(
36+
selector.EntitySelectorConfig(
37+
domain=["sensor", "input_number", "number"],
38+
)
39+
)
40+
if suggested_value:
41+
return vol.Schema(
42+
{
43+
vol.Optional(
44+
CONF_EXTERNAL_TEMP_ENTITY,
45+
description={"suggested_value": suggested_value},
46+
): entity_selector,
47+
}
48+
)
49+
return vol.Schema(
50+
{
51+
vol.Optional(CONF_EXTERNAL_TEMP_ENTITY): entity_selector,
52+
}
53+
)
54+
55+
3256
_LOGGER = logging.getLogger(__name__)
3357

3458
STEP_USER_DATA_SCHEMA = vol.Schema(
@@ -87,6 +111,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
87111

88112
def __init__(self) -> None:
89113
"""Initialize the config flow."""
114+
super().__init__()
90115
self._connection_data: dict[str, Any] = {}
91116
self._experimental_features: bool = False
92117
self._device_info: dict[str, Any] = {}
@@ -151,17 +176,7 @@ async def async_step_experimental(self, user_input: dict[str, Any] | None = None
151176
external_temp_entity = user_input.get(CONF_EXTERNAL_TEMP_ENTITY)
152177
return self._create_entry(external_temp_entity=external_temp_entity)
153178

154-
experimental_schema = vol.Schema(
155-
{
156-
vol.Optional(CONF_EXTERNAL_TEMP_ENTITY): selector.EntitySelector(
157-
selector.EntitySelectorConfig(
158-
domain=["sensor", "input_number", "number"],
159-
)
160-
),
161-
}
162-
)
163-
164-
return self.async_show_form(step_id="experimental", data_schema=experimental_schema)
179+
return self.async_show_form(step_id="experimental", data_schema=_get_experimental_schema())
165180

166181
def _create_entry(self, external_temp_entity: str | None) -> Any:
167182
"""Create the config entry with options."""
@@ -186,6 +201,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
186201

187202
def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
188203
"""Initialize options flow."""
204+
super().__init__()
189205
self._config_entry = config_entry
190206
self._connection_data: dict[str, Any] = {}
191207
self._experimental_features: bool = False
@@ -266,21 +282,11 @@ async def async_step_experimental(self, user_input: dict[str, Any] | None = None
266282

267283
current_external_temp_entity = self.config_entry.options.get(CONF_EXTERNAL_TEMP_ENTITY, "")
268284

269-
experimental_schema = vol.Schema(
270-
{
271-
vol.Optional(
272-
CONF_EXTERNAL_TEMP_ENTITY,
273-
description={"suggested_value": current_external_temp_entity},
274-
): selector.EntitySelector(
275-
selector.EntitySelectorConfig(
276-
domain=["sensor", "input_number", "number"],
277-
)
278-
),
279-
}
285+
return self.async_show_form(
286+
step_id="experimental",
287+
data_schema=_get_experimental_schema(current_external_temp_entity or None),
280288
)
281289

282-
return self.async_show_form(step_id="experimental", data_schema=experimental_schema)
283-
284290
async def _async_save_options(self, external_temp_entity: str | None) -> Any:
285291
"""Save connection data and options."""
286292
# Update the config entry data (connection settings)

custom_components/mitsubishi/coordinator.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,18 @@ def __init__(
4646
self._remote_temp_mode = config_entry.options.get(CONF_REMOTE_TEMP_MODE, False)
4747
self._startup_mode_applied = False
4848

49-
def set_remote_temp_mode(self, enabled: bool) -> None:
50-
"""Set whether remote temperature mode is enabled and persist to storage."""
49+
async def set_remote_temp_mode(self, enabled: bool) -> None:
50+
"""Set whether remote temperature mode is enabled and persist to storage.
51+
52+
When disabling remote mode, this also tells the AC to use its internal sensor.
53+
"""
5154
self._remote_temp_mode = enabled
5255
_LOGGER.info("Remote temperature mode set to: %s", enabled)
5356

57+
# When disabling remote mode, tell the AC to use internal sensor
58+
if not enabled:
59+
await self.hass.async_add_executor_job(self.controller.set_current_temperature, None)
60+
5461
# Persist to config entry options
5562
if self.config_entry is not None:
5663
new_options = dict(self.config_entry.options)
@@ -114,8 +121,7 @@ async def _send_remote_temperature(self) -> None:
114121
"Remote temperature mode enabled but no external entity configured, "
115122
"falling back to internal sensor"
116123
)
117-
await self.hass.async_add_executor_job(self.controller.set_current_temperature, None)
118-
self.set_remote_temp_mode(False)
124+
await self.set_remote_temp_mode(False)
119125
return
120126

121127
state = self.hass.states.get(external_entity_id)
@@ -125,8 +131,7 @@ async def _send_remote_temperature(self) -> None:
125131
"External temperature entity %s not found, falling back to internal sensor",
126132
external_entity_id,
127133
)
128-
await self.hass.async_add_executor_job(self.controller.set_current_temperature, None)
129-
self.set_remote_temp_mode(False)
134+
await self.set_remote_temp_mode(False)
130135
return
131136

132137
if state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
@@ -135,8 +140,7 @@ async def _send_remote_temperature(self) -> None:
135140
external_entity_id,
136141
state.state,
137142
)
138-
await self.hass.async_add_executor_job(self.controller.set_current_temperature, None)
139-
self.set_remote_temp_mode(False)
143+
await self.set_remote_temp_mode(False)
140144
return
141145

142146
try:
@@ -156,5 +160,4 @@ async def _send_remote_temperature(self) -> None:
156160
external_entity_id,
157161
e,
158162
)
159-
await self.hass.async_add_executor_job(self.controller.set_current_temperature, None)
160-
self.set_remote_temp_mode(False)
163+
await self.set_remote_temp_mode(False)

custom_components/mitsubishi/select.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,8 @@ def current_option(self) -> str | None:
103103
async def async_select_option(self, option: str) -> None:
104104
"""Set the temperature source mode."""
105105
if option == TEMP_SOURCE_INTERNAL:
106-
# Switch to internal sensor
107-
self.coordinator.set_remote_temp_mode(False)
108-
await self.hass.async_add_executor_job(
109-
self.coordinator.controller.set_current_temperature,
110-
None,
111-
)
106+
# Switch to internal sensor (set_remote_temp_mode handles the AC command)
107+
await self.coordinator.set_remote_temp_mode(False)
112108
_LOGGER.info("Switched to internal temperature sensor")
113109
else:
114110
# Switch to remote sensor - check if external entity is configured
@@ -139,7 +135,7 @@ async def async_select_option(self, option: str) -> None:
139135
return
140136

141137
# Enable remote mode and send the temperature
142-
self.coordinator.set_remote_temp_mode(True)
138+
await self.coordinator.set_remote_temp_mode(True)
143139
await self.hass.async_add_executor_job(
144140
self.coordinator.controller.set_current_temperature,
145141
temp,

tests/test_coordinator.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,14 @@ async def test_coordinator_set_remote_temp_mode(
126126
hass, mock_mitsubishi_controller, mock_config_entry
127127
)
128128

129-
coordinator.set_remote_temp_mode(True)
129+
# Enable remote mode (no hardware call when enabling)
130+
await coordinator.set_remote_temp_mode(True)
130131
assert coordinator.remote_temp_mode is True
131132

132-
coordinator.set_remote_temp_mode(False)
133+
# Disable remote mode (should call set_current_temperature(None))
134+
await coordinator.set_remote_temp_mode(False)
133135
assert coordinator.remote_temp_mode is False
136+
mock_mitsubishi_controller.set_current_temperature.assert_called_with(None)
134137

135138

136139
@pytest.fixture

tests/test_select.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,35 +184,32 @@ async def test_temperature_source_select_switch_to_internal(
184184
hass, mock_coordinator, mock_config_entry_experimental
185185
):
186186
"""Test switching temperature source to internal."""
187-
mock_coordinator.set_remote_temp_mode = MagicMock()
187+
mock_coordinator.set_remote_temp_mode = AsyncMock()
188188
select = MitsubishiTemperatureSourceSelect(mock_coordinator, mock_config_entry_experimental)
189189
select.hass = hass
190190
select.async_write_ha_state = MagicMock()
191191

192-
with patch.object(hass, "async_add_executor_job", new=AsyncMock()) as mock_executor:
193-
await select.async_select_option("Internal")
192+
await select.async_select_option("Internal")
194193

195-
mock_coordinator.set_remote_temp_mode.assert_called_once_with(False)
196-
mock_executor.assert_called_once()
197-
select.async_write_ha_state.assert_called_once()
194+
# set_remote_temp_mode(False) handles the AC command internally
195+
mock_coordinator.set_remote_temp_mode.assert_called_once_with(False)
196+
select.async_write_ha_state.assert_called_once()
198197

199198

200199
@pytest.mark.asyncio
201200
async def test_temperature_source_select_switch_to_remote_no_entity(
202201
hass, mock_coordinator, mock_config_entry
203202
):
204203
"""Test switching to remote fails when no external entity configured."""
205-
mock_coordinator.set_remote_temp_mode = MagicMock()
204+
mock_coordinator.set_remote_temp_mode = AsyncMock()
206205
select = MitsubishiTemperatureSourceSelect(mock_coordinator, mock_config_entry)
207206
select.hass = hass
208207
select.async_write_ha_state = MagicMock()
209208

210-
with patch.object(hass, "async_add_executor_job", new=AsyncMock()) as mock_executor:
211-
await select.async_select_option("Remote")
209+
await select.async_select_option("Remote")
212210

213-
# Should not change mode when no entity configured
214-
mock_coordinator.set_remote_temp_mode.assert_not_called()
215-
mock_executor.assert_not_called()
211+
# Should not change mode when no entity configured
212+
mock_coordinator.set_remote_temp_mode.assert_not_called()
216213

217214

218215
@pytest.mark.asyncio

0 commit comments

Comments
 (0)