Skip to content

Commit 7ff79fc

Browse files
authored
Merge pull request #332 from jwillemsen/jwi-horizontalswing
Add support for separate horizontal/vertical swing modes
2 parents 764e760 + e8ce0ce commit 7ff79fc

File tree

16 files changed

+486
-1782
lines changed

16 files changed

+486
-1782
lines changed

.github/workflows/constraints.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pip==24.2
1+
pip==24.3.1
22
pre-commit==3.6.2
33
black==24.2.0
44
flake8==7.0.0

.github/workflows/precommit.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Setup Python
1818
uses: "actions/setup-python@v5"
1919
with:
20-
python-version: "3.12"
20+
python-version: "3.13"
2121
- name: Upgrade pip
2222
run: |
2323
pip install --constraint=.github/workflows/constraints.txt pip

.github/workflows/tests.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Setup Python
1818
uses: "actions/setup-python@v5"
1919
with:
20-
python-version: "3.12"
20+
python-version: "3.13"
2121
- name: Install requirements
2222
run: |
2323
pip install --constraint=.github/workflows/constraints.txt pip

custom_components/daikin_onecta/climate.py

+80-98
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
from homeassistant.components.climate.const import PRESET_COMFORT
1717
from homeassistant.components.climate.const import PRESET_ECO
1818
from homeassistant.components.climate.const import PRESET_NONE
19-
from homeassistant.components.climate.const import SWING_BOTH
20-
from homeassistant.components.climate.const import SWING_HORIZONTAL
21-
from homeassistant.components.climate.const import SWING_OFF
22-
from homeassistant.components.climate.const import SWING_VERTICAL
2319
from homeassistant.const import ATTR_TEMPERATURE
2420
from homeassistant.const import CONF_HOST
2521
from homeassistant.const import CONF_NAME
@@ -31,10 +27,6 @@
3127
from .const import DAIKIN_DEVICES
3228
from .const import DOMAIN as DAIKIN_DOMAIN
3329
from .const import FANMODE_FIXED
34-
from .const import SWING_COMFORT
35-
from .const import SWING_COMFORT_HORIZONTAL
36-
from .const import SWING_FLOOR
37-
from .const import SWING_FLOOR_HORIZONTAL
3830

3931
_LOGGER = logging.getLogger(__name__)
4032

@@ -133,10 +125,12 @@ def update_state(self) -> None:
133125
self._attr_target_temperature = self.get_target_temperature()
134126
self._attr_hvac_modes = self.get_hvac_modes()
135127
self._attr_swing_modes = self.get_swing_modes()
128+
self._attr_swing_horizontal_modes = self.get_swing_horizontal_modes()
136129
self._attr_preset_modes = self.get_preset_modes()
137130
self._attr_fan_modes = self.get_fan_modes()
138131
self._attr_hvac_mode = self.get_hvac_mode()
139132
self._attr_swing_mode = self.get_swing_mode()
133+
self._attr_swing_horizontal_mode = self.get_swing_horizontal_mode()
140134
self._attr_preset_mode = self.get_preset_mode()
141135
self._attr_fan_mode = self.get_fan_mode()
142136
self._attr_device_info = self._device.device_info()
@@ -225,8 +219,12 @@ def get_supported_features(self):
225219
if operationmodedict is not None:
226220
if operationmodedict.get("fanSpeed") is not None:
227221
supported_features |= ClimateEntityFeature.FAN_MODE
228-
if operationmodedict.get("fanDirection") is not None:
229-
supported_features |= ClimateEntityFeature.SWING_MODE
222+
fan_direction = operationmodedict.get("fanDirection")
223+
if fan_direction is not None:
224+
if fan_direction.get("vertical") is not None:
225+
supported_features |= ClimateEntityFeature.SWING_MODE
226+
if fan_direction.get("horizontal") is not None:
227+
supported_features |= ClimateEntityFeature.SWING_HORIZONTAL_MODE
230228

231229
_LOGGER.info("Device '%s' supports features %s", self._device.name, supported_features)
232230

@@ -558,90 +556,64 @@ async def async_set_fan_mode(self, fan_mode):
558556

559557
return res
560558

561-
def get_swing_mode(self):
562-
swingMode = None
559+
def __get_swing_mode(self, direction):
560+
swingMode = ""
563561
cc = self.climate_control()
564562
fanControl = cc.get("fanControl")
565-
h = SWING_OFF
566-
v = SWING_OFF
567563
if fanControl is not None:
568-
swingMode = SWING_OFF
569564
operationmode = cc["operationMode"]["value"]
570565
operationmodedict = fanControl["value"]["operationModes"].get(operationmode)
571566
if operationmodedict is not None:
572567
fan_direction = operationmodedict.get("fanDirection")
573568
if fan_direction is not None:
574-
horizontal = fan_direction.get("horizontal")
575-
vertical = fan_direction.get("vertical")
576-
if horizontal is not None:
577-
h = horizontal["currentMode"]["value"]
578-
if vertical is not None:
579-
v = vertical["currentMode"]["value"]
580-
if h == "swing":
581-
swingMode = SWING_HORIZONTAL
582-
if v == "swing":
583-
swingMode = SWING_VERTICAL
584-
if v == "swing" and h == "swing":
585-
swingMode = SWING_BOTH
586-
if v == "floorHeatingAirflow":
587-
if h == "swing":
588-
swingMode = SWING_FLOOR_HORIZONTAL
589-
else:
590-
swingMode = SWING_FLOOR
591-
if v == "windNice":
592-
if h == "swing":
593-
swingMode = SWING_COMFORT_HORIZONTAL
594-
else:
595-
swingMode = SWING_COMFORT
569+
fd = fan_direction.get(direction)
570+
if fd is not None:
571+
swingMode = fd["currentMode"]["value"].lower()
596572

597573
_LOGGER.info(
598-
"Device '%s' has swing mode '%s', determined from h:%s v:%s",
574+
"Device '%s' has %s swing mode '%s'",
599575
self._device.name,
576+
direction,
600577
swingMode,
601-
h,
602-
v,
603578
)
604579

605580
return swingMode
606581

607-
def get_swing_modes(self):
582+
def get_swing_mode(self):
583+
return self.__get_swing_mode("vertical")
584+
585+
def get_swing_horizontal_mode(self):
586+
return self.__get_swing_mode("horizontal")
587+
588+
def __get_swing_modes(self, direction):
608589
swingModes = []
609590
cc = self.climate_control()
610591
fanControl = cc.get("fanControl")
611592
if fanControl is not None:
612-
swingModes = [SWING_OFF]
593+
swingModes = []
613594
operationmode = cc["operationMode"]["value"]
614595
operationmodedict = fanControl["value"]["operationModes"].get(operationmode)
615596
if operationmodedict is not None:
616597
fanDirection = operationmodedict.get("fanDirection")
617598
if fanDirection is not None:
618-
horizontal = fanDirection.get("horizontal")
619-
vertical = fanDirection.get("vertical")
620-
if horizontal is not None:
621-
for mode in horizontal["currentMode"]["values"]:
622-
if mode == "swing":
623-
swingModes.append(SWING_HORIZONTAL)
599+
vertical = fanDirection.get(direction)
624600
if vertical is not None:
625601
for mode in vertical["currentMode"]["values"]:
626-
if mode == "swing":
627-
swingModes.append(SWING_VERTICAL)
628-
if horizontal is not None:
629-
swingModes.append(SWING_BOTH)
630-
if mode == "floorHeatingAirflow":
631-
swingModes.append(SWING_FLOOR)
632-
if horizontal is not None:
633-
swingModes.append(SWING_FLOOR_HORIZONTAL)
634-
if mode == "windNice":
635-
swingModes.append(SWING_COMFORT)
636-
if horizontal is not None:
637-
swingModes.append(SWING_COMFORT_HORIZONTAL)
638-
_LOGGER.info("Device '%s' support swing modes %s", self._device.name, swingModes)
602+
swingModes.append(mode.lower())
603+
_LOGGER.info("Device '%s' support %s swing modes %s", self._device.name, direction, swingModes)
639604
return swingModes
640605

641-
async def async_set_swing_mode(self, swing_mode):
606+
def get_swing_modes(self):
607+
return self.__get_swing_modes("vertical")
608+
609+
def get_swing_horizontal_modes(self):
610+
return self.__get_swing_modes("horizontal")
611+
612+
async def __set_swing(self, direction, swing_mode):
642613
_LOGGER.debug(
643-
"Device '%s' request to set swing_mode to %s",
614+
"Device '%s' request to set swing %s mode to %s",
644615
self._device.name,
616+
direction,
645617
swing_mode,
646618
)
647619
res = True
@@ -652,51 +624,61 @@ async def async_set_swing_mode(self, swing_mode):
652624
operation_mode = cc["operationMode"]["value"]
653625
fan_direction = fan_control["value"]["operationModes"][operation_mode].get("fanDirection")
654626
if fan_direction is not None:
655-
horizontal = fan_direction.get("horizontal")
656-
vertical = fan_direction.get("vertical")
657-
if horizontal is not None:
658-
new_h_mode = "stop"
659-
if swing_mode in (SWING_HORIZONTAL, SWING_BOTH, SWING_COMFORT_HORIZONTAL, SWING_FLOOR_HORIZONTAL):
660-
new_h_mode = "swing"
661-
res &= await self._device.patch(
627+
fd = fan_direction.get(direction)
628+
if fd is not None:
629+
new_mode = "stop"
630+
# For translation the current mode is always lower case, but we need to send
631+
# the daikin mixed case mode, so search that
632+
for mode in fd["currentMode"]["values"]:
633+
if swing_mode == mode.lower():
634+
new_mode = mode
635+
res = await self._device.patch(
662636
self._device.id,
663637
self._embedded_id,
664638
"fanControl",
665-
f"/operationModes/{operation_mode}/fanDirection/horizontal/currentMode",
666-
new_h_mode,
639+
f"/operationModes/{operation_mode}/fanDirection/{direction}/currentMode",
640+
new_mode,
667641
)
668642
if res is False:
669643
_LOGGER.warning(
670-
"Device '%s' problem setting horizontal swing mode to %s",
644+
"Device '%s' problem setting %s swing mode to %s",
671645
self._device.name,
672-
new_h_mode,
646+
direction,
647+
new_mode,
673648
)
649+
return res
674650

675-
if vertical is not None:
676-
new_v_mode = "stop"
677-
if swing_mode in (SWING_VERTICAL, SWING_BOTH):
678-
new_v_mode = "swing"
679-
if swing_mode in (SWING_FLOOR, SWING_FLOOR_HORIZONTAL):
680-
new_v_mode = "floorHeatingAirflow"
681-
if swing_mode in (SWING_COMFORT, SWING_COMFORT_HORIZONTAL):
682-
new_v_mode = "windNice"
683-
res &= await self._device.patch(
684-
self._device.id,
685-
self._embedded_id,
686-
"fanControl",
687-
f"/operationModes/{operation_mode}/fanDirection/vertical/currentMode",
688-
new_v_mode,
689-
)
690-
if res is False:
691-
_LOGGER.warning(
692-
"Device '%s' problem setting vertical swing mode to %s",
693-
self._device.name,
694-
new_v_mode,
695-
)
651+
async def async_set_swing_mode(self, swing_mode):
652+
res = True
653+
if self.swing_mode != swing_mode:
654+
res = await self.__set_swing("vertical", swing_mode)
696655

697-
if res is True:
698-
self._attr_swing_mode = swing_mode
699-
self.async_write_ha_state()
656+
if res is True:
657+
self._attr_swing_mode = swing_mode
658+
self.async_write_ha_state()
659+
else:
660+
_LOGGER.debug(
661+
"Device '%s' request to set vertical swing mode '%s' ignored already set",
662+
self._device.name,
663+
swing_mode,
664+
)
665+
666+
return res
667+
668+
async def async_set_swing_horizontal_mode(self, swing_mode):
669+
res = True
670+
if self.swing_horizontal_mode != swing_mode:
671+
res = await self.__set_swing("horizontal", swing_mode)
672+
673+
if res is True:
674+
self._attr_swing_horizontal_mode = swing_mode
675+
self.async_write_ha_state()
676+
else:
677+
_LOGGER.debug(
678+
"Device '%s' request to set horizontal swing mode '%s' ignored already set",
679+
self._device.name,
680+
swing_mode,
681+
)
700682

701683
return res
702684

custom_components/daikin_onecta/const.py

-5
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@
2929

3030
FANMODE_FIXED = "fixed"
3131

32-
SWING_FLOOR = "floor"
33-
SWING_FLOOR_HORIZONTAL = "floor_horizontal"
34-
SWING_COMFORT = "comfort"
35-
SWING_COMFORT_HORIZONTAL = "comfort_horizontal"
36-
3732
SENSOR_PERIOD_DAILY = "d"
3833
SENSOR_PERIOD_WEEKLY = "w"
3934
SENSOR_PERIOD_YEARLY = "m"

custom_components/daikin_onecta/icons.json

+10-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,18 @@
1414
"5": "mdi:numeric-5"
1515
}
1616
},
17+
"swing_horizontal_mode": {
18+
"state": {
19+
"stop": "mdi:arrow-oscillating-off",
20+
"swing": "mdi:arrow-left-right"
21+
}
22+
},
1723
"swing_mode": {
1824
"state": {
19-
"comfort": "mdi:waves",
20-
"comfort_horizontal": "mdi:waves-arrow-left"
25+
"stop": "mdi:arrow-oscillating-off",
26+
"swing": "mdi:arrow-up-down",
27+
"windnice": "mdi:waves",
28+
"floorheatingairflow": "mdi:arrow-right-bottom"
2129
}
2230
},
2331
"preset_mode": {

custom_components/daikin_onecta/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
"iot_class": "cloud_polling",
99
"issue_tracker": "https://github.com/jwillemsen/daikin_onecta/issues",
1010
"requirements": [],
11-
"version": "4.1.23"
11+
"version": "4.2.0"
1212
}

custom_components/daikin_onecta/translations/el.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,8 @@
6161
},
6262
"swing_mode": {
6363
"state": {
64-
"floor": "Ροή Αέρα Θέρμανσης Δαπέδου",
65-
"floor_horizontal": "Ροή Αέρα Θέρμανσης Δαπέδου και Οριζόντια",
66-
"comfort": "Άνετη Ροή Αέρα",
67-
"comfort_horizontal": "Άνετη Ροή Αέρα και Οριζόντια"
64+
"floorheatingairflow": "Ροή Αέρα Θέρμανσης Δαπέδου",
65+
"windnice": "Άνετη Ροή Αέρα"
6866
}
6967
}
7068
}

custom_components/daikin_onecta/translations/en.json

+10-4
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,16 @@
6161
},
6262
"swing_mode": {
6363
"state": {
64-
"floor": "FloorHeating Airflow",
65-
"floor_horizontal": "FloorHeating Airflow and Horizontal",
66-
"comfort": "Comfort Airflow",
67-
"comfort_horizontal": "Comfort Airflow and Horizontal"
64+
"stop": "Stop",
65+
"swing": "Swing",
66+
"floorheatingairflow": "FloorHeating Airflow",
67+
"windnice": "Comfort Airflow"
68+
}
69+
},
70+
"swing_horizontal_mode": {
71+
"state": {
72+
"stop": "Stop",
73+
"swing": "Swing"
6874
}
6975
}
7076
}

custom_components/daikin_onecta/translations/nl.json

+10-4
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,16 @@
4545
},
4646
"swing_mode": {
4747
"state": {
48-
"floor": "Vloer Heating Airflow",
49-
"floor_horizontal": "Vloer Heating Airflow en Horizontaal",
50-
"comfort": "Comfort Airflow",
51-
"comfort_horizontal": "Comfort Airflow en Horizontaal"
48+
"stop": "Draaien uit",
49+
"swing": "Draaien",
50+
"floorheatingairflow": "Vloer Heating Airflow",
51+
"windnice": "Comfort Airflow"
52+
}
53+
},
54+
"swing_horizontal_mode": {
55+
"state": {
56+
"stop": "Draaien uit",
57+
"swing": "Draaien"
5258
}
5359
}
5460
}

custom_components/daikin_onecta/translations/sl.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,8 @@
6161
},
6262
"swing_mode": {
6363
"state": {
64-
"floor": "Tlačni tok ogrevanja",
65-
"floor_horizontal": "Tlačni tok ogrevanja in vodoravni",
66-
"comfort": "Tok udobja",
67-
"comfort_horizontal": "Tok udobja in vodoravni"
64+
"floorheatingairflow": "Tlačni tok ogrevanja",
65+
"windnice": "Tok udobja"
6866
}
6967
}
7068
}

0 commit comments

Comments
 (0)