Compare commits

...

9 Commits

13 changed files with 279 additions and 304 deletions

157
README.md
View File

@ -30,6 +30,40 @@ _Restart Home Assistant_
**Method 2**: Settings > Devices & Services > Add Integration > **Haier hOn** **Method 2**: Settings > Devices & Services > Add Integration > **Haier hOn**
_If the integration is not in the list, you need to clear the browser cache._ _If the integration is not in the list, you need to clear the browser cache._
## Supported Models
Support was confirmed for these models. If a supported model is missing, please [add it with this form](https://forms.gle/bTSD8qFotdZFytbf8).
- Haier WD90-B14TEAM5
- Haier HD80-A3959
- Haier HWO60SM2F3XH
- Hoover H-WASH 500
- Candy CIS633SCTTWIFI
- Haier XIB 3B2SFS-80
- Haier XIB 6B2D3FB
- Hoover HSOT3161WG
## Supported Languages
Translation of internal names like programs are available for all languages which are official supported by the hOn app:
* 🇨🇳 Chinese
* 🇭🇷 Croatian
* 🇨🇿 Czech
* 🇳🇱 Dutch
* 🇬🇧 English
* 🇫🇷 French
* 🇩🇪 German
* 🇬🇷 Greek
* 🇮🇱 Hebrew
* 🇮🇹 Italian
* 🇵🇱 Polish
* 🇵🇹 Portuguese
* 🇷🇴 Romanian
* 🇷🇺 Russian
* 🇷🇸 Serbian
* 🇸🇰 Slovak
* 🇸🇮 Slovenian
* 🇪🇸 Spanish
* 🇹🇷 Turkish
## Contribute ## Contribute
Any kind of contribution is welcome! Any kind of contribution is welcome!
### Read out device data ### Read out device data
@ -94,38 +128,6 @@ For every device exists a hidden button which can be used to log all info of you
- If you need to implement some more logic, create a pull request to the underlying library. There we collect special requirements in the `appliances` directory. - If you need to implement some more logic, create a pull request to the underlying library. There we collect special requirements in the `appliances` directory.
- Use [pyhOn's translate command](https://github.com/Andre0512/pyhOn#translation) to read out the official translations - Use [pyhOn's translate command](https://github.com/Andre0512/pyhOn#translation) to read out the official translations
## Tested Devices
- Haier WD90-B14TEAM5
- Haier HD80-A3959
- Haier HWO60SM2F3XH
- Hoover H-WASH 500
- Candy CIS633SCTTWIFI
- Haier XIB 3B2SFS-80
- Haier XIB 6B2D3FB
## Supported Languages
Translation of internal names like programs are available for all languages which are official supported by the hOn app:
* 🇨🇳 Chinese
* 🇭🇷 Croatian
* 🇨🇿 Czech
* 🇳🇱 Dutch
* 🇬🇧 English
* 🇫🇷 French
* 🇩🇪 German
* 🇬🇷 Greek
* 🇮🇱 Hebrew
* 🇮🇹 Italian
* 🇵🇱 Polish
* 🇵🇹 Portuguese
* 🇷🇴 Romanian
* 🇷🇺 Russian
* 🇷🇸 Serbian
* 🇸🇰 Slovak
* 🇸🇮 Slovenian
* 🇪🇸 Spanish
* 🇹🇷 Turkish
## About this Repo ## About this Repo
The existing integrations missed some features from the app I liked to have in HomeAssistant. The existing integrations missed some features from the app I liked to have in HomeAssistant.
I tried to create a pull request, but in the structures of these existing repos, I find it hard to fit in my needs, so I basically rewrote everything. I tried to create a pull request, but in the structures of these existing repos, I find it hard to fit in my needs, so I basically rewrote everything.
@ -137,19 +139,19 @@ I moved the api related stuff into the package [pyhOn](https://github.com/Andre0
#### Configs #### Configs
| Name | Icon | Entity | Key | | Name | Icon | Entity | Key |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 10° Heating | | `switch` | `startProgram.10degreeHeatingStatus` | | 10° Heating | `heat-wave` | `switch` | `settings.10degreeHeatingStatus` |
| Echo | | `switch` | `startProgram.echoStatus` | | Echo | `account-voice` | `switch` | `settings.echoStatus` |
| Eco Mode | | `switch` | `startProgram.ecoMode` | | Eco Mode | | `switch` | `settings.ecoMode` |
| Eco Pilot | | `select` | `startProgram.humanSensingStatus` | | Eco Pilot | `run` | `select` | `settings.humanSensingStatus` |
| Health Mode | | `switch` | `startProgram.healthMode` | | Health Mode | `medication-outline` | `switch` | `settings.healthMode` |
| Mute | | `switch` | `startProgram.muteStatus` | | Mute | `volume-off` | `switch` | `settings.muteStatus` |
| Program | | `select` | `startProgram.program` | | Program | | `select` | `startProgram.program` |
| Rapid Mode | | `switch` | `startProgram.rapidMode` | | Rapid Mode | `run-fast` | `switch` | `settings.rapidMode` |
| Screen Display | | `switch` | `startProgram.screenDisplayStatus` | | Screen Display | `monitor-small` | `switch` | `settings.screenDisplayStatus` |
| Self Cleaning | | `switch` | `startProgram.selfCleaningStatus` | | Self Cleaning | `air-filter` | `switch` | `settings.selfCleaningStatus` |
| Self Cleaning 56 | | `switch` | `startProgram.selfCleaning56Status` | | Self Cleaning 56 | `air-filter` | `switch` | `settings.selfCleaning56Status` |
| Silent Sleep | | `switch` | `startProgram.silentSleepStatus` | | Silent Sleep | `bed` | `switch` | `settings.silentSleepStatus` |
| Target Temperature | `thermometer` | `number` | `startProgram.tempSel` | | Target Temperature | `thermometer` | `number` | `settings.tempSel` |
### Dish washer ### Dish washer
#### Controls #### Controls
@ -208,7 +210,6 @@ I moved the api related stuff into the package [pyhOn](https://github.com/Andre0
| Pan Status | `pot-mix` | `binary_sensor` | `panStatus` | | Pan Status | `pot-mix` | `binary_sensor` | `panStatus` |
| Power | `lightning-bolt` | `sensor` | `power` | | Power | `lightning-bolt` | `sensor` | `power` |
| Remaining Time | `timer` | `sensor` | `remainingTimeMM` | | Remaining Time | `timer` | `sensor` | `remainingTimeMM` |
| Remote Control | `remote` | `binary_sensor` | `attributes.parameters.remoteCtrValid` |
| Temperature | `thermometer` | `sensor` | `temp` | | Temperature | `thermometer` | `sensor` | `temp` |
### Oven ### Oven
@ -230,7 +231,6 @@ I moved the api related stuff into the package [pyhOn](https://github.com/Andre0
| Connection | `wifi` | `binary_sensor` | `attributes.lastConnEvent.category` | | Connection | `wifi` | `binary_sensor` | `attributes.lastConnEvent.category` |
| On | `power-cycle` | `binary_sensor` | `attributes.parameters.onOffStatus` | | On | `power-cycle` | `binary_sensor` | `attributes.parameters.onOffStatus` |
| Remaining Time | `timer` | `sensor` | `remainingTimeMM` | | Remaining Time | `timer` | `sensor` | `remainingTimeMM` |
| Remote Control | `remote` | `binary_sensor` | `attributes.parameters.remoteCtrValid` |
| Start Time | `clock-start` | `sensor` | `delayTime` | | Start Time | `clock-start` | `sensor` | `delayTime` |
| Temperature | `thermometer` | `sensor` | `temp` | | Temperature | `thermometer` | `sensor` | `temp` |
| Temperature Selected | `thermometer` | `sensor` | `tempSel` | | Temperature Selected | `thermometer` | `sensor` | `tempSel` |
@ -253,58 +253,96 @@ I moved the api related stuff into the package [pyhOn](https://github.com/Andre0
| Energy Label | `lightning-bolt-circle` | `sensor` | `startProgram.energyLabel` | | Energy Label | `lightning-bolt-circle` | `sensor` | `startProgram.energyLabel` |
| Program | | `select` | `startProgram.program` | | Program | | `select` | `startProgram.program` |
| Steam Type | `weather-dust` | `sensor` | `steamType` | | Steam Type | `weather-dust` | `sensor` | `steamType` |
| Steam level | `smoke` | `sensor` | `steamLevel` | | Steam level | `smoke` | `sensor` | `startProgram.steamLevel` |
| Sterilization | `clock-start` | `switch` | `startProgram.sterilizationStatus` | | Sterilization | `clock-start` | `switch` | `startProgram.sterilizationStatus` |
| Suggested Load | `weight-kilogram` | `sensor` | `startProgram.suggestedLoadD` | | Suggested Load | `weight-kilogram` | `sensor` | `startProgram.suggestedLoadD` |
| Temperature level | `thermometer` | `number` | `startProgram.tempLevel` | | Temperature level | `thermometer` | `number` | `startProgram.tempLevel` |
#### Sensors #### Sensors
| Name | Icon | Entity | Key | | Name | Icon | Entity | Key |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Anti-Crease | | `binary_sensor` | `anticrease` |
| Connection | | `binary_sensor` | `attributes.lastConnEvent.category` | | Connection | | `binary_sensor` | `attributes.lastConnEvent.category` |
| Door | | `binary_sensor` | `doorStatus` | | Door | | `binary_sensor` | `doorStatus` |
| Dry level | `hair-dryer` | `sensor` | `dryLevel` | | Dry level | `hair-dryer` | `sensor` | `dryLevel` |
| Error | `math-log` | `sensor` | `errors` | | Error | `math-log` | `sensor` | `errors` |
| Machine Status | `information` | `sensor` | `machMode` | | Machine Status | `information` | `sensor` | `machMode` |
| Program | `tumble-dryer` | `sensor` | `prCode` | | Program | `tumble-dryer` | `sensor` | `programName` |
| Program Phase | `washing-machine` | `sensor` | `prPhase` | | Program Phase | `washing-machine` | `sensor` | `prPhase` |
| Remaining Time | `timer` | `sensor` | `remainingTimeMM` | | Remaining Time | `timer` | `sensor` | `remainingTimeMM` |
| Start Time | `clock-start` | `sensor` | `delayTime` | | Start Time | `clock-start` | `sensor` | `delayTime` |
| Steam level | `smoke` | `sensor` | `steamLevel` |
| Temperature level | `thermometer` | `sensor` | `tempLevel` | | Temperature level | `thermometer` | `sensor` | `tempLevel` |
### Washer dryer ### Washer dryer
#### Controls #### Controls
| Name | Icon | Entity | Key | | Name | Icon | Entity | Key |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Pause Washing Machine | `pause` | `switch` | `pauseProgram` / `resumeProgram` | | Pause Washer Dryer | `pause` | `switch` | `pauseProgram` / `resumeProgram` |
| Washing Machine | `washing-machine` | `switch` | `startProgram` / `stopProgram` | | Washer Dryer | `washing-machine` | `switch` | `startProgram` / `stopProgram` |
#### Configs #### Configs
| Name | Icon | Entity | Key | | Name | Icon | Entity | Key |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Acqua Plus | `water-plus` | `switch` | `startProgram.acquaplus` |
| Anti-Crease | `timer` | `switch` | `startProgram.antiCreaseTime` |
| Anti-Crease | `timer` | `switch` | `startProgram.anticrease` |
| Auto Dose | `cup` | `switch` | `startProgram.autoDetergentStatus` |
| Delay Status | `timer-check` | `switch` | `startProgram.delayStatus` |
| Delay Time | `timer-plus` | `number` | `startProgram.delayTime` | | Delay Time | `timer-plus` | `number` | `startProgram.delayTime` |
| Dry Time | | `number` | `startProgram.dryTime` |
| Dry Time | `timer` | `select` | `startProgram.dryTimeMM` |
| Dry level | `hair-dryer` | `select` | `startProgram.dryLevel` |
| Energy Label | `lightning-bolt-circle` | `sensor` | `startProgram.energyLabel` |
| Extra Rinse 1 | `numeric-1-box-multiple-outline` | `switch` | `startProgram.extraRinse1` |
| Extra Rinse 2 | `numeric-2-box-multiple-outline` | `switch` | `startProgram.extraRinse2` |
| Extra Rinse 3 | `numeric-3-box-multiple-outline` | `switch` | `startProgram.extraRinse3` |
| Good Night | `weather-night` | `switch` | `startProgram.goodNight` |
| Keep Fresh | `refresh-circle` | `switch` | `startProgram.autoSoftenerStatus` |
| Liquid Detergent Dose | `cup-water` | `sensor` | `startProgram.liquidDetergentDose` |
| Main Wash Time | `clock-start` | `number` | `startProgram.mainWashTime` |
| Powder Detergent Dose | `cup` | `sensor` | `startProgram.powderDetergentDose` |
| Program | | `select` | `startProgram.program` | | Program | | `select` | `startProgram.program` |
| Remaining Time | `timer` | `sensor` | `startProgram.remainingTime` |
| Rinse Iterations | `rotate-right` | `number` | `startProgram.rinseIterations` |
| Soak Prewash Selection | `tshirt-crew` | `switch` | `startProgram.haier_SoakPrewashSelection` |
| Spin speed | `numeric` | `select` | `startProgram.spinSpeed` |
| Steam Level | `weather-dust` | `number` | `startProgram.steamLevel` |
| Steam Type | `weather-dust` | `sensor` | `steamType` |
| Steam level | `smoke` | `sensor` | `startProgram.steamLevel` |
| Sterilization | `clock-start` | `switch` | `startProgram.sterilizationStatus` |
| Suggested Load | `weight-kilogram` | `sensor` | `startProgram.suggestedLoadW` |
| Suggested Load | `weight-kilogram` | `sensor` | `startProgram.suggestedLoadD` |
| Suggested weight | `weight-kilogram` | `sensor` | `startProgram.weight` | | Suggested weight | `weight-kilogram` | `sensor` | `startProgram.weight` |
| Temperature | `thermometer` | `select` | `startProgram.temp` |
| Temperature level | `thermometer` | `number` | `startProgram.tempLevel` |
| Water hard | `water` | `number` | `startProgram.waterHard` |
| lang | | `number` | `startProgram.lang` |
#### Sensors #### Sensors
| Name | Icon | Entity | Key | | Name | Icon | Entity | Key |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Acqua Plus | | `binary_sensor` | `acquaplus` | | Acqua Plus | | `binary_sensor` | `acquaplus` |
| Anti-Crease | | `binary_sensor` | `anticrease` | | Anti-Crease | | `binary_sensor` | `anticrease` |
| Current Electricity Used | `lightning-bolt` | `sensor` | `currentElectricityUsed` | | Current Electricity Used | `lightning-bolt` | `sensor` | `currentElectricityUsed` |
| Current Program | `tumble-dryer` | `sensor` | `prCode` |
| Current Temperature | `thermometer` | `sensor` | `temp` | | Current Temperature | `thermometer` | `sensor` | `temp` |
| Current Water Used | `water` | `sensor` | `currentWaterUsed` | | Current Water Used | `water` | `sensor` | `currentWaterUsed` |
| Dirt level | `liquid-spot` | `sensor` | `dirtyLevel` | | Dirt level | `liquid-spot` | `sensor` | `dirtyLevel` |
| Door | | `binary_sensor` | `doorStatus` |
| Door Lock | | `binary_sensor` | `doorLockStatus` |
| Dry level | `hair-dryer` | `sensor` | `dryLevel` | | Dry level | `hair-dryer` | `sensor` | `dryLevel` |
| Error | `math-log` | `sensor` | `errors` |
| Extra Rinse 1 | | `binary_sensor` | `extraRinse1` | | Extra Rinse 1 | | `binary_sensor` | `extraRinse1` |
| Extra Rinse 2 | | `binary_sensor` | `extraRinse2` | | Extra Rinse 2 | | `binary_sensor` | `extraRinse2` |
| Extra Rinse 3 | | `binary_sensor` | `extraRinse3` | | Extra Rinse 3 | | `binary_sensor` | `extraRinse3` |
| Good Night Mode | | `binary_sensor` | `goodNight` | | Good Night Mode | | `binary_sensor` | `goodNight` |
| Machine Status | `information` | `sensor` | `machMode` | | Machine Status | `information` | `sensor` | `machMode` |
| Pre Wash | | `binary_sensor` | `startProgram.prewash` | | Pre Wash | | `binary_sensor` | `startProgram.prewash` |
| Program | `tumble-dryer` | `sensor` | `programName` |
| Program Phase | `washing-machine` | `sensor` | `prPhase` | | Program Phase | `washing-machine` | `sensor` | `prPhase` |
| Remaining Time | `timer` | `sensor` | `remainingTimeMM` | | Remaining Time | `timer` | `sensor` | `remainingTimeMM` |
| Remote Control | `remote` | `binary_sensor` | `attributes.lastConnEvent.category` | | Remote Control | `remote` | `binary_sensor` | `attributes.lastConnEvent.category` |
| Spin Speed | `fast-forward-outline` | `sensor` | `spinSpeed` | | Spin Speed | `speedometer` | `sensor` | `spinSpeed` |
| Start Time | `clock-start` | `sensor` | `delayTime` |
| Steam level | `smoke` | `sensor` | `steamLevel` | | Steam level | `smoke` | `sensor` | `steamLevel` |
| Temperature level | `thermometer` | `sensor` | `tempLevel` |
| Total Power | | `sensor` | `totalElectricityUsed` | | Total Power | | `sensor` | `totalElectricityUsed` |
| Total Wash Cycle | `counter` | `sensor` | `totalWashCycle` | | Total Wash Cycle | `counter` | `sensor` | `totalWashCycle` |
| Total Water | | `sensor` | `totalWaterUsed` | | Total Water | | `sensor` | `totalWaterUsed` |
@ -323,10 +361,10 @@ I moved the api related stuff into the package [pyhOn](https://github.com/Andre0
| Delay Status | `timer-check` | `switch` | `startProgram.delayStatus` | | Delay Status | `timer-check` | `switch` | `startProgram.delayStatus` |
| Delay Time | `timer-plus` | `number` | `startProgram.delayTime` | | Delay Time | `timer-plus` | `number` | `startProgram.delayTime` |
| Energy Label | `lightning-bolt-circle` | `sensor` | `startProgram.energyLabel` | | Energy Label | `lightning-bolt-circle` | `sensor` | `startProgram.energyLabel` |
| Extra Rinse 1 | `numeric-1-box-multiple-outline` | `switch` | `extraRinse1` | | Extra Rinse 1 | `numeric-1-box-multiple-outline` | `switch` | `startProgram.extraRinse1` |
| Extra Rinse 2 | `numeric-2-box-multiple-outline` | `switch` | `extraRinse2` | | Extra Rinse 2 | `numeric-2-box-multiple-outline` | `switch` | `startProgram.extraRinse2` |
| Extra Rinse 3 | `numeric-3-box-multiple-outline` | `switch` | `extraRinse3` | | Extra Rinse 3 | `numeric-3-box-multiple-outline` | `switch` | `startProgram.extraRinse3` |
| Good Night | `weather-night` | `switch` | `goodNight` | | Good Night | `weather-night` | `switch` | `startProgram.goodNight` |
| Keep Fresh | `refresh-circle` | `switch` | `startProgram.autoSoftenerStatus` | | Keep Fresh | `refresh-circle` | `switch` | `startProgram.autoSoftenerStatus` |
| Liquid Detergent Dose | `cup-water` | `sensor` | `startProgram.liquidDetergentDose` | | Liquid Detergent Dose | `cup-water` | `sensor` | `startProgram.liquidDetergentDose` |
| Main Wash Time | `clock-start` | `number` | `startProgram.mainWashTime` | | Main Wash Time | `clock-start` | `number` | `startProgram.mainWashTime` |
@ -345,13 +383,20 @@ I moved the api related stuff into the package [pyhOn](https://github.com/Andre0
#### Sensors #### Sensors
| Name | Icon | Entity | Key | | Name | Icon | Entity | Key |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Acqua Plus | | `binary_sensor` | `acquaplus` |
| Current Electricity Used | `lightning-bolt` | `sensor` | `currentElectricityUsed` | | Current Electricity Used | `lightning-bolt` | `sensor` | `currentElectricityUsed` |
| Current Temperature | `thermometer` | `sensor` | `temp` |
| Current Water Used | `water` | `sensor` | `currentWaterUsed` | | Current Water Used | `water` | `sensor` | `currentWaterUsed` |
| Dirt level | `liquid-spot` | `sensor` | `dirtyLevel` | | Dirt level | `liquid-spot` | `sensor` | `dirtyLevel` |
| Door | | `binary_sensor` | `doorStatus` | | Door | | `binary_sensor` | `doorStatus` |
| Door Lock | | `binary_sensor` | `doorLockStatus` | | Door Lock | | `binary_sensor` | `doorLockStatus` |
| Error | `math-log` | `sensor` | `errors` | | Error | `math-log` | `sensor` | `errors` |
| Extra Rinse 1 | | `binary_sensor` | `extraRinse1` |
| Extra Rinse 2 | | `binary_sensor` | `extraRinse2` |
| Extra Rinse 3 | | `binary_sensor` | `extraRinse3` |
| Good Night Mode | | `binary_sensor` | `goodNight` |
| Machine Status | `information` | `sensor` | `machMode` | | Machine Status | `information` | `sensor` | `machMode` |
| Pre Wash | | `binary_sensor` | `startProgram.prewash` |
| Program Phase | `washing-machine` | `sensor` | `prPhase` | | Program Phase | `washing-machine` | `sensor` | `prPhase` |
| Remaining Time | `timer` | `sensor` | `remainingTimeMM` | | Remaining Time | `timer` | `sensor` | `remainingTimeMM` |
| Remote Control | `remote` | `binary_sensor` | `attributes.lastConnEvent.category` | | Remote Control | `remote` | `binary_sensor` | `attributes.lastConnEvent.category` |

View File

@ -11,7 +11,7 @@ from homeassistant.components.binary_sensor import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import callback from homeassistant.core import callback
from .const import DOMAIN from .const import DOMAIN
from .hon import HonCoordinator, HonEntity from .hon import HonCoordinator, HonEntity, unique_entities
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -52,32 +52,6 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = {
on_value="1", on_value="1",
translation_key="door_open", translation_key="door_open",
), ),
),
"TD": (
HonBinarySensorEntityDescription(
key="attributes.lastConnEvent.category",
name="Connection",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
on_value="CONNECTED",
translation_key="connection",
),
HonBinarySensorEntityDescription(
key="doorStatus",
name="Door",
device_class=BinarySensorDeviceClass.DOOR,
on_value="1",
translation_key="door_open",
),
),
"WD": (
HonBinarySensorEntityDescription(
key="attributes.lastConnEvent.category",
name="Remote Control",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
on_value="CONNECTED",
icon="mdi:remote",
translation_key="remote_control",
),
HonBinarySensorEntityDescription( HonBinarySensorEntityDescription(
key="startProgram.prewash", name="Pre Wash", translation_key="prewash" key="startProgram.prewash", name="Pre Wash", translation_key="prewash"
), ),
@ -96,6 +70,22 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = {
HonBinarySensorEntityDescription( HonBinarySensorEntityDescription(
key="acquaplus", name="Acqua Plus", translation_key="acqua_plus" key="acquaplus", name="Acqua Plus", translation_key="acqua_plus"
), ),
),
"TD": (
HonBinarySensorEntityDescription(
key="attributes.lastConnEvent.category",
name="Connection",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
on_value="CONNECTED",
translation_key="connection",
),
HonBinarySensorEntityDescription(
key="doorStatus",
name="Door",
device_class=BinarySensorDeviceClass.DOOR,
on_value="1",
translation_key="door_open",
),
HonBinarySensorEntityDescription( HonBinarySensorEntityDescription(
key="anticrease", name="Anti-Crease", translation_key="anti_crease" key="anticrease", name="Anti-Crease", translation_key="anti_crease"
), ),
@ -109,14 +99,6 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = {
icon="mdi:wifi", icon="mdi:wifi",
translation_key="connection", translation_key="connection",
), ),
HonBinarySensorEntityDescription(
key="attributes.parameters.remoteCtrValid",
name="Remote Control",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
on_value="1",
icon="mdi:remote",
translation_key="remote_control",
),
HonBinarySensorEntityDescription( HonBinarySensorEntityDescription(
key="attributes.parameters.onOffStatus", key="attributes.parameters.onOffStatus",
name="On", name="On",
@ -135,14 +117,6 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = {
icon="mdi:wifi", icon="mdi:wifi",
translation_key="connection", translation_key="connection",
), ),
HonBinarySensorEntityDescription(
key="attributes.parameters.remoteCtrValid",
name="Remote Control",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
on_value="1",
icon="mdi:remote",
translation_key="remote_control",
),
HonBinarySensorEntityDescription( HonBinarySensorEntityDescription(
key="attributes.parameters.onOffStatus", key="attributes.parameters.onOffStatus",
name="On", name="On",
@ -208,6 +182,9 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = {
} }
BINARY_SENSORS["WD"] = unique_entities(BINARY_SENSORS["WM"], BINARY_SENSORS["TD"])
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
hon: Hon = hass.data[DOMAIN][entry.unique_id] hon: Hon = hass.data[DOMAIN][entry.unique_id]
coordinators = hass.data[DOMAIN]["coordinators"] coordinators = hass.data[DOMAIN]["coordinators"]
@ -224,12 +201,8 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non
for description in descriptions: for description in descriptions:
if not device.get(description.key): if not device.get(description.key):
continue continue
appliances.extend( appliances.append(
[ HonBinarySensorEntity(hass, coordinator, entry, device, description)
HonBinarySensorEntity(
hass, coordinator, entry, device, description
)
]
) )
async_add_entities(appliances) async_add_entities(appliances)

View File

@ -64,6 +64,11 @@ class HonButtonEntity(HonEntity, ButtonEntity):
async def async_press(self) -> None: async def async_press(self) -> None:
await self._device.commands[self.entity_description.key].send() await self._device.commands[self.entity_description.key].send()
@property
def available(self) -> bool:
"""Return True if entity is available."""
return super().available and self._device.get("remoteCtrValid") == "1"
class HonFeatureRequestButton(HonEntity, ButtonEntity): class HonFeatureRequestButton(HonEntity, ButtonEntity):
def __init__(self, hass, coordinator, entry, device: HonAppliance) -> None: def __init__(self, hass, coordinator, entry, device: HonAppliance) -> None:

View File

@ -29,7 +29,7 @@ from .hon import HonEntity, HonCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CLIMATES = { CLIMATES = {
"AC": (ClimateEntityDescription(key="startProgram"),), "AC": (ClimateEntityDescription(key="startProgram", icon="mdi:air-conditioner"),),
} }
@ -47,7 +47,7 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non
if descriptions := CLIMATES.get(device.appliance_type): if descriptions := CLIMATES.get(device.appliance_type):
for description in descriptions: for description in descriptions:
if description.key not in device.available_settings: if description.key not in list(device.commands):
continue continue
appliances.extend( appliances.extend(
[HonClimateEntity(hass, coordinator, entry, device, description)] [HonClimateEntity(hass, coordinator, entry, device, description)]
@ -61,21 +61,21 @@ class HonClimateEntity(HonEntity, ClimateEntity):
) -> None: ) -> None:
super().__init__(hass, entry, coordinator, device) super().__init__(hass, entry, coordinator, device)
self._coordinator = coordinator self._coordinator = coordinator
self._device = coordinator.device self._device = device
self.entity_description = description self.entity_description = description
self._hass = hass self._hass = hass
self._attr_unique_id = f"{super().unique_id}climate" self._attr_unique_id = f"{super().unique_id}climate"
self._attr_temperature_unit = TEMP_CELSIUS self._attr_temperature_unit = TEMP_CELSIUS
self._attr_target_temperature_step = PRECISION_WHOLE self._attr_target_temperature_step = PRECISION_WHOLE
self._attr_max_temp = device.settings["tempSel"].max self._attr_max_temp = device.settings["settings.tempSel"].max
self._attr_min_temp = device.settings["tempSel"].min self._attr_min_temp = device.settings["settings.tempSel"].min
self._attr_hvac_modes = [HVACMode.OFF] + [ self._attr_hvac_modes = [HVACMode.OFF] + [
HON_HVAC_MODE[mode] for mode in device.settings["machMode"].values HON_HVAC_MODE[mode] for mode in device.settings["settings.machMode"].values
] ]
self._attr_fan_modes = [FAN_OFF] + [ self._attr_fan_modes = [FAN_OFF] + [
HON_FAN[mode] for mode in device.settings["windSpeed"].values HON_FAN[mode] for mode in device.settings["settings.windSpeed"].values
] ]
self._attr_swing_modes = [ self._attr_swing_modes = [
SWING_OFF, SWING_OFF,
@ -89,22 +89,28 @@ class HonClimateEntity(HonEntity, ClimateEntity):
| ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.SWING_MODE
) )
self._handle_coordinator_update()
async def async_set_hvac_mode(self, hvac_mode): async def async_set_hvac_mode(self, hvac_mode):
if hvac_mode == HVACMode.OFF: if hvac_mode == HVACMode.OFF:
self._device.commands["stopProgram"].send() await self._device.commands["stopProgram"].send()
else: else:
self._device.settings["program"].value = HON_HVAC_PROGRAM[hvac_mode] self._device.settings["startProgram.program"].value = HON_HVAC_PROGRAM[
self._device.commands["startProgram"].send() hvac_mode
]
await self._device.commands["startProgram"].send()
self._attr_hvac_mode = hvac_mode self._attr_hvac_mode = hvac_mode
async def async_set_fan_mode(self, fan_mode): async def async_set_fan_mode(self, fan_mode):
mode_number = list(HON_FAN.values()).index(fan_mode) mode_number = list(HON_FAN.values()).index(fan_mode)
self._device.settings["windSpeed"].value = list(HON_FAN.keys())[mode_number] self._device.settings["settings.windSpeed"].value = list(HON_FAN.keys())[
self._device.commands["startProgram"].send() mode_number
]
await self._device.commands["settings"].send()
async def async_set_swing_mode(self, swing_mode): async def async_set_swing_mode(self, swing_mode):
horizontal = self._device.settings["windDirectionHorizontal"] horizontal = self._device.settings["settings.windDirectionHorizontal"]
vertical = self._device.settings["windDirectionVertical"] vertical = self._device.settings["settings.windDirectionVertical"]
if swing_mode in [SWING_BOTH, SWING_HORIZONTAL]: if swing_mode in [SWING_BOTH, SWING_HORIZONTAL]:
horizontal.value = "7" horizontal.value = "7"
if swing_mode in [SWING_BOTH, SWING_VERTICAL]: if swing_mode in [SWING_BOTH, SWING_VERTICAL]:
@ -114,30 +120,30 @@ class HonClimateEntity(HonEntity, ClimateEntity):
if swing_mode in [SWING_OFF, SWING_VERTICAL] and horizontal.value == "7": if swing_mode in [SWING_OFF, SWING_VERTICAL] and horizontal.value == "7":
horizontal.value = "0" horizontal.value = "0"
self._attr_swing_mode = swing_mode self._attr_swing_mode = swing_mode
self._device.commands["startProgram"].send() await self._device.commands["settings"].send()
async def async_set_temperature(self, **kwargs): async def async_set_temperature(self, **kwargs):
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
return False return False
self._device.settings["selTemp"].value = temperature self._device.settings["settings.selTemp"].value = temperature
self._device.commands["startProgram"].send() await self._device.commands["settings"].send()
@callback @callback
def _handle_coordinator_update(self, update=True) -> None: def _handle_coordinator_update(self, update=True) -> None:
self._attr_target_temperature = int(float(self._device.get("tempSel"))) self._attr_target_temperature = int(float(self._device.get("tempSel")))
self._attr_current_temperature = float(self._device.get("tempIndoor")) self._attr_current_temperature = float(self._device.get("tempIndoor"))
self._attr_max_temp = self._device.settings["tempSel"].max self._attr_max_temp = self._device.settings["settings.tempSel"].max
self._attr_min_temp = self._device.settings["tempSel"].min self._attr_min_temp = self._device.settings["settings.tempSel"].min
if self._device.get("onOffStatus") == "0": if self._device.get("onOffStatus") == "0":
self._attr_hvac_mode = HVACMode.OFF self._attr_hvac_mode = HVACMode.OFF
else: else:
self._attr_hvac_mode = HON_HVAC_MODE[self._device.get("machMode")] self._attr_hvac_mode = HON_HVAC_MODE[self._device.get("machMode") or "0"]
self._attr_fan_mode = HON_FAN[self._device.settings["windSpeed"].value] self._attr_fan_mode = HON_FAN[self._device.settings["settings.windSpeed"].value]
horizontal = self._device.settings["windDirectionHorizontal"] horizontal = self._device.settings["settings.windDirectionHorizontal"]
vertical = self._device.settings["windDirectionVertical"] vertical = self._device.settings["settings.windDirectionVertical"]
if horizontal == "7" and vertical == "8": if horizontal == "7" and vertical == "8":
self._attr_swing_mode = SWING_BOTH self._attr_swing_mode = SWING_BOTH
elif horizontal == "7": elif horizontal == "7":

View File

@ -120,6 +120,19 @@ TUMBLE_DRYER_PR_PHASE = {
"19": "WASHING_CMD&CTRL.PHASE_DRYING.TITLE", "19": "WASHING_CMD&CTRL.PHASE_DRYING.TITLE",
"20": "WASHING_CMD&CTRL.PHASE_DRYING.TITLE", "20": "WASHING_CMD&CTRL.PHASE_DRYING.TITLE",
} }
DIRTY_LEVEL = {
"1": "WASHING_CMD&CTRL.PROGRAM_CYCLE_DETAIL_OPTIONS_VALUES_DESCRIPTION.LITTLE",
"2": "WASHING_CMD&CTRL.PROGRAM_CYCLE_DETAIL_OPTIONS_VALUES_DESCRIPTION.NORMAL",
"3": "WASHING_CMD&CTRL.PROGRAM_CYCLE_DETAIL_OPTIONS_VALUES_DESCRIPTION.VERY",
}
STEAM_LEVEL = {
"0": "WASHING_CMD&CTRL.PROGRAM_CYCLE_DETAIL_OPTIONS_VALUES_DESCRIPTION.NO_STEAM",
"1": "WASHING_CMD&CTRL.GUIDED_WASHING_SYMBOLS_FABRICS.COTTON_TITLE",
"2": "WASHING_CMD&CTRL.GUIDED_WASHING_SYMBOLS_FABRICS.DELICATE_TITLE",
"3": "WASHING_CMD&CTRL.GUIDED_WASHING_SYMBOLS_FABRICS.SYNTHETIC_TITLE",
}
DISHWASHER_PR_PHASE = { DISHWASHER_PR_PHASE = {
"0": "WASHING_CMD&CTRL.PHASE_READY.TITLE", "0": "WASHING_CMD&CTRL.PHASE_READY.TITLE",
"1": "WASHING_CMD&CTRL.PHASE_PREWASH.TITLE", "1": "WASHING_CMD&CTRL.PHASE_PREWASH.TITLE",

View File

@ -50,3 +50,12 @@ class HonCoordinator(DataUpdateCoordinator):
async def _async_update_data(self): async def _async_update_data(self):
await self._device.update() await self._device.update()
def unique_entities(base_entities, new_entities):
result = list(base_entities)
existing_entities = [entity.key for entity in base_entities]
for entity in new_entities:
if entity.key not in existing_entities:
result.append(entity)
return tuple(result)

View File

@ -6,6 +6,6 @@
"documentation": "https://github.com/Andre0512/hon/", "documentation": "https://github.com/Andre0512/hon/",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"issue_tracker": "https://github.com/Andre0512/hon/issues", "issue_tracker": "https://github.com/Andre0512/hon/issues",
"requirements": ["pyhOn==0.10.3"], "requirements": ["pyhOn==0.10.6"],
"version": "0.7.0" "version": "0.7.3-beta.2"
} }

View File

@ -15,7 +15,7 @@ from homeassistant.core import callback
from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity import EntityCategory
from .const import DOMAIN from .const import DOMAIN
from .hon import HonEntity, HonCoordinator from .hon import HonEntity, HonCoordinator, unique_entities
NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = {
"WM": ( "WM": (
@ -85,16 +85,6 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = {
translation_key="dry_time", translation_key="dry_time",
), ),
), ),
"WD": (
NumberEntityDescription(
key="startProgram.delayTime",
name="Delay Time",
icon="mdi:timer-plus",
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=UnitOfTime.MINUTES,
translation_key="delay_time",
),
),
"OV": ( "OV": (
NumberEntityDescription( NumberEntityDescription(
key="startProgram.delayTime", key="startProgram.delayTime",
@ -156,7 +146,7 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = {
), ),
"AC": ( "AC": (
NumberEntityDescription( NumberEntityDescription(
key="startProgram.tempSel", key="settings.tempSel",
name="Target Temperature", name="Target Temperature",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
icon="mdi:thermometer", icon="mdi:thermometer",
@ -166,6 +156,8 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = {
), ),
} }
NUMBERS["WD"] = unique_entities(NUMBERS["WM"], NUMBERS["TD"])
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
hon: Hon = hass.data[DOMAIN][entry.unique_id] hon: Hon = hass.data[DOMAIN][entry.unique_id]

View File

@ -14,7 +14,7 @@ from homeassistant.core import callback
from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity import EntityCategory
from .const import DOMAIN from .const import DOMAIN
from .hon import HonEntity, HonCoordinator from .hon import HonEntity, HonCoordinator, unique_entities
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -66,14 +66,6 @@ SELECTS = {
translation_key="dry_levels", translation_key="dry_levels",
), ),
), ),
"WD": (
SelectEntityDescription(
key="startProgram.program",
name="Program",
entity_category=EntityCategory.CONFIG,
translation_key="programs_wm",
),
),
"OV": ( "OV": (
SelectEntityDescription( SelectEntityDescription(
key="startProgram.program", key="startProgram.program",
@ -106,14 +98,17 @@ SELECTS = {
translation_key="programs_ac", translation_key="programs_ac",
), ),
SelectEntityDescription( SelectEntityDescription(
key="startProgram.humanSensingStatus", key="settings.humanSensingStatus",
name="Eco Pilot", name="Eco Pilot",
icon="mdi:run",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="eco_pilot", translation_key="eco_pilot",
), ),
), ),
} }
SELECTS["WD"] = unique_entities(SELECTS["WM"], SELECTS["TD"])
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
hon: Hon = hass.data[DOMAIN][entry.unique_id] hon: Hon = hass.data[DOMAIN][entry.unique_id]

View File

@ -25,7 +25,7 @@ from homeassistant.const import PERCENTAGE
from . import const from . import const
from .const import DOMAIN from .const import DOMAIN
from .hon import HonCoordinator, HonEntity from .hon import HonCoordinator, HonEntity, unique_entities
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -112,7 +112,7 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = {
name="Spin Speed", name="Spin Speed",
icon="mdi:speedometer", icon="mdi:speedometer",
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTime.MINUTES, native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
translation_key="spin_speed", translation_key="spin_speed",
), ),
SensorEntityDescription( SensorEntityDescription(
@ -161,6 +161,14 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = {
native_unit_of_measurement=UnitOfMass.KILOGRAMS, native_unit_of_measurement=UnitOfMass.KILOGRAMS,
translation_key="suggested_load", translation_key="suggested_load",
), ),
SensorEntityDescription(
key="temp",
name="Current Temperature",
icon="mdi:thermometer",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
translation_key="temperature",
),
), ),
"TD": ( "TD": (
SensorEntityDescription( SensorEntityDescription(
@ -238,12 +246,18 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = {
translation_key="energy_label", translation_key="energy_label",
), ),
SensorEntityDescription( SensorEntityDescription(
key="steamLevel", key="startProgram.steamLevel",
name="Steam level", name="Steam level",
icon="mdi:smoke", icon="mdi:smoke",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="steam_level", translation_key="steam_level",
), ),
SensorEntityDescription(
key="steamLevel",
name="Steam level",
icon="mdi:smoke",
translation_key="steam_level",
),
SensorEntityDescription( SensorEntityDescription(
key="steamType", key="steamType",
name="Steam Type", name="Steam Type",
@ -251,120 +265,6 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = {
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
), ),
"WD": (
SensorEntityDescription(
key="totalElectricityUsed",
name="Total Power",
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
translation_key="energy_total",
),
SensorEntityDescription(
key="totalWaterUsed",
name="Total Water",
device_class=SensorDeviceClass.WATER,
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfVolume.LITERS,
translation_key="water_total",
),
SensorEntityDescription(
key="totalWashCycle",
name="Total Wash Cycle",
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:counter",
translation_key="cycles_total",
),
SensorEntityDescription(
key="currentElectricityUsed",
name="Current Electricity Used",
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.KILO_WATT,
icon="mdi:lightning-bolt",
translation_key="energy_current",
),
SensorEntityDescription(
key="currentWaterUsed",
name="Current Water Used",
state_class=SensorStateClass.MEASUREMENT,
icon="mdi:water",
translation_key="water_current",
),
SensorEntityDescription(
key="startProgram.weight",
name="Suggested weight",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
icon="mdi:weight-kilogram",
translation_key="suggested_load",
),
SensorEntityDescription(
key="machMode",
name="Machine Status",
icon="mdi:information",
device_class=SensorDeviceClass.ENUM,
translation_key="washing_modes",
options=list(const.MACH_MODE),
),
SensorEntityDescription(
key="spinSpeed",
name="Spin Speed",
icon="mdi:fast-forward-outline",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
translation_key="spin_speed",
),
SensorEntityDescription(
key="remainingTimeMM",
name="Remaining Time",
icon="mdi:timer",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTime.MINUTES,
translation_key="remaining_time",
),
SensorEntityDescription(
key="prCode",
name="Current Program",
icon="mdi:tumble-dryer",
translation_key="programs",
),
SensorEntityDescription(
key="prPhase",
name="Program Phase",
icon="mdi:washing-machine",
device_class=SensorDeviceClass.ENUM,
translation_key="program_phases_wm",
options=list(const.WASHING_PR_PHASE),
),
SensorEntityDescription(
key="dryLevel",
name="Dry level",
icon="mdi:hair-dryer",
translation_key="dry_levels",
),
SensorEntityDescription(
key="dirtyLevel",
name="Dirt level",
icon="mdi:liquid-spot",
translation_key="dirt_level",
),
SensorEntityDescription(
key="steamLevel",
name="Steam level",
icon="mdi:smoke",
translation_key="steam_level",
),
SensorEntityDescription(
key="temp",
name="Current Temperature",
icon="mdi:thermometer",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
translation_key="temperature",
),
),
"OV": ( "OV": (
SensorEntityDescription( SensorEntityDescription(
key="remainingTimeMM", key="remainingTimeMM",
@ -501,6 +401,7 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = {
), ),
), ),
} }
SENSORS["WD"] = unique_entities(SENSORS["WM"], SENSORS["TD"])
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:

View File

@ -10,7 +10,7 @@ from pyhon.appliance import HonAppliance
from pyhon.parameter.range import HonParameterRange from pyhon.parameter.range import HonParameterRange
from .const import DOMAIN from .const import DOMAIN
from .hon import HonCoordinator, HonEntity from .hon import HonCoordinator, HonEntity, unique_entities
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -82,28 +82,28 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = {
translation_key="acqua_plus", translation_key="acqua_plus",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="extraRinse1", key="startProgram.extraRinse1",
name="Extra Rinse 1", name="Extra Rinse 1",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
icon="mdi:numeric-1-box-multiple-outline", icon="mdi:numeric-1-box-multiple-outline",
translation_key="extra_rinse_1", translation_key="extra_rinse_1",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="extraRinse2", key="startProgram.extraRinse2",
name="Extra Rinse 2", name="Extra Rinse 2",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
icon="mdi:numeric-2-box-multiple-outline", icon="mdi:numeric-2-box-multiple-outline",
translation_key="extra_rinse_2", translation_key="extra_rinse_2",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="extraRinse3", key="startProgram.extraRinse3",
name="Extra Rinse 3", name="Extra Rinse 3",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
icon="mdi:numeric-3-box-multiple-outline", icon="mdi:numeric-3-box-multiple-outline",
translation_key="extra_rinse_3", translation_key="extra_rinse_3",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="goodNight", key="startProgram.goodNight",
name="Good Night", name="Good Night",
icon="mdi:weather-night", icon="mdi:weather-night",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
@ -168,7 +168,7 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = {
"WD": ( "WD": (
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="active", key="active",
name="Washing Machine", name="Washer Dryer",
icon="mdi:washing-machine", icon="mdi:washing-machine",
turn_on_key="startProgram", turn_on_key="startProgram",
turn_off_key="stopProgram", turn_off_key="stopProgram",
@ -176,7 +176,7 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = {
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="pause", key="pause",
name="Pause Washing Machine", name="Pause Washer Dryer",
icon="mdi:pause", icon="mdi:pause",
turn_on_key="pauseProgram", turn_on_key="pauseProgram",
turn_off_key="resumeProgram", turn_off_key="resumeProgram",
@ -237,65 +237,77 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = {
), ),
"AC": ( "AC": (
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.10degreeHeatingStatus", key="settings.10degreeHeatingStatus",
name="10° Heating", name="10° Heating",
icon="mdi:heat-wave",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="10_degree_heating", translation_key="10_degree_heating",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.echoStatus", key="settings.echoStatus",
name="Echo", name="Echo",
icon="mdi:account-voice",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.ecoMode", key="settings.ecoMode",
name="Eco Mode", name="Eco Mode",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="eco_mode", translation_key="eco_mode",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.healthMode", key="settings.healthMode",
name="Health Mode", name="Health Mode",
icon="mdi:medication-outline",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.muteStatus", key="settings.muteStatus",
name="Mute", name="Mute",
icon="mdi:volume-off",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="mute_mode", translation_key="mute_mode",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.rapidMode", key="settings.rapidMode",
name="Rapid Mode", name="Rapid Mode",
icon="mdi:run-fast",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="rapid_mode", translation_key="rapid_mode",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.screenDisplayStatus", key="settings.screenDisplayStatus",
name="Screen Display", name="Screen Display",
icon="mdi:monitor-small",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.selfCleaning56Status", key="settings.selfCleaning56Status",
name="Self Cleaning 56", name="Self Cleaning 56",
icon="mdi:air-filter",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="self_clean_56", translation_key="self_clean_56",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.selfCleaningStatus", key="settings.selfCleaningStatus",
name="Self Cleaning", name="Self Cleaning",
icon="mdi:air-filter",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="self_clean", translation_key="self_clean",
), ),
HonSwitchEntityDescription( HonSwitchEntityDescription(
key="startProgram.silentSleepStatus", key="settings.silentSleepStatus",
name="Silent Sleep", name="Silent Sleep",
icon="mdi:bed",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
translation_key="silent_mode", translation_key="silent_mode",
), ),
), ),
} }
SWITCHES["WD"] = unique_entities(SWITCHES["WD"], SWITCHES["WM"])
SWITCHES["WD"] = unique_entities(SWITCHES["WD"], SWITCHES["TD"])
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
hon: Hon = hass.data[DOMAIN][entry.unique_id] hon: Hon = hass.data[DOMAIN][entry.unique_id]
@ -311,10 +323,18 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non
if descriptions := SWITCHES.get(device.appliance_type): if descriptions := SWITCHES.get(device.appliance_type):
for description in descriptions: for description in descriptions:
if ( if description.entity_category == EntityCategory.CONFIG:
device.get(description.key) is not None if description.key not in device.available_settings:
or description.key in device.available_settings continue
else:
if not any(
[
device.get(description.key) is not None,
description.turn_on_key in list(device.commands),
description.turn_off_key in list(device.commands),
]
): ):
continue
appliances.extend( appliances.extend(
[HonSwitchEntity(hass, coordinator, entry, device, description)] [HonSwitchEntity(hass, coordinator, entry, device, description)]
) )
@ -376,3 +396,11 @@ class HonSwitchEntity(HonEntity, SwitchEntity):
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
else: else:
await self._device.commands[self.entity_description.turn_off_key].send() await self._device.commands[self.entity_description.turn_off_key].send()
@property
def available(self) -> bool:
"""Return True if entity is available."""
if self.entity_category == EntityCategory.CONFIG:
return super().available
else:
return super().available and self._device.get("remoteCtrValid") == "1"

24
info.md
View File

@ -13,12 +13,6 @@ Support for home appliances of Haier's mobile app hOn.
- [Dish Washer](https://github.com/Andre0512/hon#dish-washer) - [Dish Washer](https://github.com/Andre0512/hon#dish-washer)
- [Air conditioner](https://github.com/Andre0512/hon#air-conditioner) [BETA] - [Air conditioner](https://github.com/Andre0512/hon#air-conditioner) [BETA]
## Tested Appliances
- Haier WD90-B14TEAM5
- Haier HD80-A3959
- Haier HWO60SM2F3XH
- Hoover H-WASH 500
## Configuration ## Configuration
**Method 1**: [![Open your Home Assistant instance and start setting up a new integration.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=hon) **Method 1**: [![Open your Home Assistant instance and start setting up a new integration.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=hon)
@ -26,9 +20,16 @@ Support for home appliances of Haier's mobile app hOn.
**Method 2**: Settings > Devices & Services > Add Integration > **Haier hOn** **Method 2**: Settings > Devices & Services > Add Integration > **Haier hOn**
_If the integration is not in the list, you need to clear the browser cache._ _If the integration is not in the list, you need to clear the browser cache._
## Contribute ## Supported Models
Want to help us to support more appliances? Or add more sensors? Or help with translating? Or beautify some icons or captions? Support was confirmed for these models. If a supported model is missing, please [add it with this form](https://forms.gle/bTSD8qFotdZFytbf8).
Check out the [project on GitHub](https://github.com/Andre0512/hon), every contribution is welcome! - Haier WD90-B14TEAM5
- Haier HD80-A3959
- Haier HWO60SM2F3XH
- Hoover H-WASH 500
- Candy CIS633SCTTWIFI
- Haier XIB 3B2SFS-80
- Haier XIB 6B2D3FB
- Hoover HSOT3161WG
## Supported Languages ## Supported Languages
Translation of internal names like programs are available for all languages which are official supported by the hOn app: Translation of internal names like programs are available for all languages which are official supported by the hOn app:
@ -52,10 +53,13 @@ Translation of internal names like programs are available for all languages whic
* 🇪🇸 Spanish * 🇪🇸 Spanish
* 🇹🇷 Turkish * 🇹🇷 Turkish
## Contribute
Want to help us to support more appliances? Or add more sensors? Or help with translating? Or beautify some icons or captions?
Check out the [project on GitHub](https://github.com/Andre0512/hon), every contribution is welcome!
## Useful Links ## Useful Links
* [GitHub repository](https://github.com/Andre0512/hon) (please add a star if you like this integration!) * [GitHub repository](https://github.com/Andre0512/hon) (please add a star if you like this integration!)
* [pyhOn library](https://github.com/Andre0512/pyhOn) * [pyhOn library](https://github.com/Andre0512/pyhOn)
* [Release notes](https://github.com/Andre0512/hon/releases) * [Release notes](https://github.com/Andre0512/hon/releases)
* [Discussion and help](https://github.com/Andre0512/hon/discussions) * [Discussion and help](https://github.com/Andre0512/hon/discussions)
* [Issues](https://github.com/Andre0512/hon/issues) * [Issues](https://github.com/Andre0512/hon/issues)

View File

@ -3,10 +3,14 @@
import asyncio import asyncio
import json import json
import re import re
import sys
from pathlib import Path from pathlib import Path
from pyhon import HonAPI from pyhon import HonAPI
if __name__ == "__main__":
sys.path.insert(0, str(Path(__file__).parent.parent))
from custom_components.hon import const from custom_components.hon import const
SENSOR = { SENSOR = {