Compare commits
	
		
			1 Commits
		
	
	
		
			v0.6.0-bet
			...
			v0.6.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 28e23c7f3c | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +1,3 @@ | ||||
| __pycache__/ | ||||
| scripts/test.py | ||||
| test.py | ||||
| .idea/ | ||||
|  | ||||
							
								
								
									
										188
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										188
									
								
								README.md
									
									
									
									
									
								
							| @ -4,14 +4,12 @@ | ||||
|  | ||||
| [](https://analytics.home-assistant.io/)   | ||||
| Home Assistant integration for Haier hOn: support for Haier/Candy/Hoover home appliances like washing machines. | ||||
|  | ||||
| ## Supported Appliances | ||||
| - [Washing Machine](https://github.com/Andre0512/hon#washing-machine) | ||||
| - [Tumble Dryer](https://github.com/Andre0512/hon#tumble-dryer) | ||||
| - [Washer Dryer](https://github.com/Andre0512/hon#washer-dryer) | ||||
| - [Oven](https://github.com/Andre0512/hon#oven) | ||||
| - [Hob](https://github.com/Andre0512/hon#hob) | ||||
| - [Dish Washer](https://github.com/Andre0512/hon#dish-washer) | ||||
| - Tumble Dryer | ||||
| - Washer Dryer | ||||
| - Washing Machine | ||||
| - Oven | ||||
| - Hob | ||||
|  | ||||
| ## Installation | ||||
| **Method 1:** [](https://my.home-assistant.io/redirect/hacs_repository/?owner=Andre0512&repository=hon&category=integration) | ||||
| @ -29,6 +27,8 @@ _Restart Home Assistant_ | ||||
| **Method 2**: Settings > Devices & Services > Add Integration > **Haier hOn**   | ||||
| _If the integration is not in the list, you need to clear the browser cache._ | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Contribute | ||||
| Any kind of contribution is welcome! | ||||
| ### Read out device data | ||||
| @ -103,177 +103,3 @@ For every device exists a hidden button which can be used to log all info of you | ||||
| 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 moved the api related stuff into the package [pyhOn](https://github.com/Andre0512/pyhOn). | ||||
|  | ||||
| ## Appliance Features | ||||
| ### Dish washer | ||||
| #### Controls | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Dish Washer | `mdi:dishwasher` | `switch` | `active` | | ||||
| #### Configs | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Add Dish | `mdi:silverware-fork-knife` | `switch` | `startProgram.addDish` | | ||||
| | Delay time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||
| | Eco Express | `mdi:sprout` | `switch` | `startProgram.ecoExpress` | | ||||
| | Eco Index | `mdi:sprout` | `sensor` | `startProgram.ecoIndex` | | ||||
| | Energy Label | `mdi:lightning-bolt-circle` | `sensor` | `startProgram.energyLabel` | | ||||
| | Extra Dry | `mdi:hair-dryer` | `switch` | `startProgram.extraDry` | | ||||
| | Half Load | `mdi:fraction-one-half` | `switch` | `startProgram.halfLoad` | | ||||
| | Open Door | `mdi:door-open` | `switch` | `startProgram.openDoor` | | ||||
| | Program |  | `select` | `startProgram.program` | | ||||
| | Temperature | `mdi:thermometer` | `sensor` | `startProgram.temp` | | ||||
| | Three in One | `mdi:numeric-3-box-outline` | `switch` | `startProgram.threeInOne` | | ||||
| | Time | `mdi:timer` | `sensor` | `startProgram.remainingTime` | | ||||
| | Water Efficiency | `mdi:water` | `sensor` | `startProgram.waterEfficiency` | | ||||
| | Water Saving | `mdi:water-percent` | `sensor` | `startProgram.waterSaving` | | ||||
| | Water hard | `mdi:water` | `number` | `startProgram.waterHard` | | ||||
| #### Sensors | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Connection |  | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||
| | Door |  | `binary_sensor` | `doorStatus` | | ||||
| | Error | `mdi:math-log` | `sensor` | `errors` | | ||||
| | Machine Status | `mdi:information` | `sensor` | `machMode` | | ||||
| | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||
| | Rinse Aid | `mdi:spray-bottle` | `binary_sensor` | `rinseAidStatus` | | ||||
| | Salt | `mdi:shaker-outline` | `binary_sensor` | `saltStatus` | | ||||
| ### Hob | ||||
| #### Controls | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Start Program | `mdi:pot-steam` | `button` | `startProgram` | | ||||
| #### Configs | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Power Management | `mdi:timelapse` | `number` | `startProgram.powerManagement` | | ||||
| | Program |  | `select` | `startProgram.program` | | ||||
| | Temperature | `mdi:thermometer` | `number` | `startProgram.temp` | | ||||
| #### Sensors | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Connection | `mdi:wifi` | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||
| | Error | `mdi:math-log` | `sensor` | `errors` | | ||||
| | Hob Lock |  | `binary_sensor` | `hobLockStatus` | | ||||
| | Hot Status |  | `binary_sensor` | `hotStatus` | | ||||
| | On | `mdi:power-cycle` | `binary_sensor` | `attributes.parameters.onOffStatus` | | ||||
| | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||
| | Remote Control | `mdi:remote` | `binary_sensor` | `attributes.parameters.remoteCtrValid` | | ||||
| | Temperature | `mdi:thermometer` | `sensor` | `temp` | | ||||
| ### Oven | ||||
| #### Controls | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Start Program | `mdi:power-cycle` | `button` | `startProgram` | | ||||
| | Stop Program | `mdi:power-off` | `button` | `stopProgram` | | ||||
| #### Configs | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Delay time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||
| | Preheat |  | `select` | `startProgram.preheatStatus` | | ||||
| | Program |  | `select` | `startProgram.program` | | ||||
| | Program Duration | `mdi:timelapse` | `number` | `startProgram.prTime` | | ||||
| | Target Temperature | `mdi:thermometer` | `number` | `startProgram.tempSel` | | ||||
| #### Sensors | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Connection | `mdi:wifi` | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||
| | On | `mdi:power-cycle` | `binary_sensor` | `attributes.parameters.onOffStatus` | | ||||
| | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||
| | Remote Control | `mdi:remote` | `binary_sensor` | `attributes.parameters.remoteCtrValid` | | ||||
| | Start Time | `mdi:clock-start` | `sensor` | `delayTime` | | ||||
| | Temperature | `mdi:thermometer` | `sensor` | `temp` | | ||||
| | Temperature Selected | `mdi:thermometer` | `sensor` | `tempSel` | | ||||
| ### Tumble dryer | ||||
| #### Controls | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Pause Tumble Dryer | `mdi:pause` | `switch` | `pause` | | ||||
| | Tumble Dryer | `mdi:tumble-dryer` | `switch` | `active` | | ||||
| #### Configs | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Anti-Crease time | `mdi:timer` | `number` | `startProgram.antiCreaseTime` | | ||||
| | Delay time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||
| | Dry level | `mdi:hair-dryer` | `number` | `startProgram.dryLevel` | | ||||
| | Program |  | `select` | `startProgram.program` | | ||||
| | Sterilization status | `mdi:clock-start` | `number` | `startProgram.sterilizationStatus` | | ||||
| | Temperature level | `mdi:thermometer` | `number` | `startProgram.tempLevel` | | ||||
| | Time | `mdi:timer` | `select` | `startProgram.dryTimeMM` | | ||||
| #### Sensors | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Connection |  | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||
| | Door |  | `binary_sensor` | `doorStatus` | | ||||
| | Dry level | `mdi:hair-dryer` | `sensor` | `dryLevel` | | ||||
| | Error | `mdi:math-log` | `sensor` | `errors` | | ||||
| | Machine Status | `mdi:information` | `sensor` | `machMode` | | ||||
| | Program | `mdi:tumble-dryer` | `sensor` | `prCode` | | ||||
| | Program Phase | `mdi:tumble-dryer` | `sensor` | `prPhase` | | ||||
| | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||
| | Start Time | `mdi:clock-start` | `sensor` | `delayTime` | | ||||
| | Temperature level | `mdi:thermometer` | `sensor` | `tempLevel` | | ||||
| ### Washer dryer | ||||
| #### Controls | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Pause Washing Machine | `mdi:pause` | `switch` | `pause` | | ||||
| | Washing Machine | `mdi:washing-machine` | `switch` | `active` | | ||||
| #### Configs | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Delay Time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||
| | Program |  | `select` | `startProgram.program` | | ||||
| #### Sensors | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Acqua Plus |  | `binary_sensor` | `acquaplus` | | ||||
| | Anti-Crease |  | `binary_sensor` | `anticrease` | | ||||
| | Current Program | `mdi:tumble-dryer` | `sensor` | `prCode` | | ||||
| | Current Temperature | `mdi:thermometer` | `sensor` | `temp` | | ||||
| | Dirt level | `mdi:liquid-spot` | `sensor` | `dirtyLevel` | | ||||
| | Dry level | `mdi:hair-dryer` | `sensor` | `dryLevel` | | ||||
| | 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 | `mdi:information` | `sensor` | `machMode` | | ||||
| | Pre Wash |  | `binary_sensor` | `startProgram.prewash` | | ||||
| | Program Phase | `mdi:tumble-dryer` | `sensor` | `prPhase` | | ||||
| | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||
| | Remote Control | `mdi:remote` | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||
| | Spin Speed | `mdi:fast-forward-outline` | `sensor` | `spinSpeed` | | ||||
| | Steam level | `mdi:smoke` | `sensor` | `steamLevel` | | ||||
| ### Washing machine | ||||
| #### Controls | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Pause Washing Machine | `mdi:pause` | `switch` | `pause` | | ||||
| | Washing Machine | `mdi:washing-machine` | `switch` | `active` | | ||||
| #### Configs | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Delay Status | `mdi:timer-check` | `switch` | `startProgram.delayStatus` | | ||||
| | Delay Time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||
| | Main Wash Time | `mdi:clock-start` | `number` | `startProgram.mainWashTime` | | ||||
| | Program |  | `select` | `startProgram.program` | | ||||
| | Rinse Iterations | `mdi:rotate-right` | `number` | `startProgram.rinseIterations` | | ||||
| | Soak Prewash Selection | `mdi:tshirt-crew` | `switch` | `startProgram.haier_SoakPrewashSelection` | | ||||
| | Spin speed | `mdi:numeric` | `select` | `startProgram.spinSpeed` | | ||||
| | Suggested weight | `mdi:weight-kilogram` | `sensor` | `startProgram.weight` | | ||||
| | Temperature | `mdi:thermometer` | `select` | `startProgram.temp` | | ||||
| #### Sensors | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Current Electricity Used | `mdi:lightning-bolt` | `sensor` | `currentElectricityUsed` | | ||||
| | Current Water Used | `mdi:water` | `sensor` | `currentWaterUsed` | | ||||
| | Door |  | `binary_sensor` | `doorStatus` | | ||||
| | Door Lock |  | `binary_sensor` | `doorLockStatus` | | ||||
| | Error | `mdi:math-log` | `sensor` | `errors` | | ||||
| | Machine Status | `mdi:information` | `sensor` | `machMode` | | ||||
| | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||
| | Remote Control | `mdi:remote` | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||
| | Spin Speed | `mdi:speedometer` | `sensor` | `spinSpeed` | | ||||
| | Total Power |  | `sensor` | `totalElectricityUsed` | | ||||
| | Total Wash Cycle | `mdi:counter` | `sensor` | `totalWashCycle` | | ||||
| | Total Water |  | `sensor` | `totalWaterUsed` | | ||||
|  | ||||
| @ -124,7 +124,7 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = { | ||||
|             icon="mdi:power-cycle", | ||||
|         ), | ||||
|     ), | ||||
|     "IH": ( | ||||
|     "IV": ( | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="attributes.lastConnEvent.category", | ||||
|             name="Connection", | ||||
| @ -159,34 +159,6 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = { | ||||
|             on_value="0", | ||||
|         ), | ||||
|     ), | ||||
|     "DW": ( | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="saltStatus", | ||||
|             name="Salt", | ||||
|             device_class=BinarySensorDeviceClass.PROBLEM, | ||||
|             on_value="1", | ||||
|             icon="mdi:shaker-outline", | ||||
|         ), | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="rinseAidStatus", | ||||
|             name="Rinse Aid", | ||||
|             device_class=BinarySensorDeviceClass.PROBLEM, | ||||
|             on_value="1", | ||||
|             icon="mdi:spray-bottle", | ||||
|         ), | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="attributes.lastConnEvent.category", | ||||
|             name="Connection", | ||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||
|             on_value="CONNECTED", | ||||
|         ), | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="doorStatus", | ||||
|             name="Door", | ||||
|             device_class=BinarySensorDeviceClass.DOOR, | ||||
|             on_value="1", | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -195,11 +167,11 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         else: | ||||
|             coordinator = HonCoordinator(hass, device) | ||||
|             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator | ||||
|         await coordinator.async_config_entry_first_refresh() | ||||
|  | ||||
|         if descriptions := BINARY_SENSORS.get(device.appliance_type): | ||||
|  | ||||
| @ -26,7 +26,7 @@ BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = { | ||||
|             icon="mdi:power-off", | ||||
|         ), | ||||
|     ), | ||||
|     "IH": ( | ||||
|     "IV": ( | ||||
|         ButtonEntityDescription( | ||||
|             key="startProgram", | ||||
|             name="Start Program", | ||||
| @ -41,11 +41,11 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         else: | ||||
|             coordinator = HonCoordinator(hass, device) | ||||
|             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator | ||||
|         await coordinator.async_config_entry_first_refresh() | ||||
|  | ||||
|         if descriptions := BUTTONS.get(device.appliance_type): | ||||
|  | ||||
| @ -22,12 +22,12 @@ class HonEntity(CoordinatorEntity): | ||||
|         self._hass = hass | ||||
|         self._device = device | ||||
|  | ||||
|         self._attr_unique_id = self._device.unique_id | ||||
|         self._attr_unique_id = self._device.mac_address | ||||
|  | ||||
|     @property | ||||
|     def device_info(self): | ||||
|         return DeviceInfo( | ||||
|             identifiers={(DOMAIN, self._device.unique_id)}, | ||||
|             identifiers={(DOMAIN, self._device.mac_address)}, | ||||
|             manufacturer=self._device.get("brand", ""), | ||||
|             name=self._device.nick_name | ||||
|             if self._device.nick_name | ||||
| @ -43,7 +43,7 @@ class HonCoordinator(DataUpdateCoordinator): | ||||
|         super().__init__( | ||||
|             hass, | ||||
|             _LOGGER, | ||||
|             name=device.unique_id, | ||||
|             name=device.mac_address, | ||||
|             update_interval=timedelta(seconds=30), | ||||
|         ) | ||||
|         self._device = device | ||||
|  | ||||
| @ -6,6 +6,6 @@ | ||||
|   "documentation": "https://github.com/Andre0512/hon/", | ||||
|   "iot_class": "cloud_polling", | ||||
|   "issue_tracker": "https://github.com/Andre0512/hon/issues", | ||||
|   "requirements": ["pyhOn==0.8.0b7"], | ||||
|   "version": "0.6.0-beta.7" | ||||
|   "requirements": ["pyhOn==0.8.0b2"], | ||||
|   "version": "0.6.0-beta.1" | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| from __future__ import annotations | ||||
|  | ||||
| from pyhon import Hon | ||||
| from pyhon.parameter.range import HonParameterRange | ||||
| from pyhon.parameter import HonParameterRange | ||||
|  | ||||
| from homeassistant.components.number import ( | ||||
|     NumberEntity, | ||||
| @ -106,7 +106,7 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|     ), | ||||
|     "IH": ( | ||||
|     "IV": ( | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.temp", | ||||
|             name="Temperature", | ||||
| @ -120,21 +120,6 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | ||||
|             icon="mdi:timelapse", | ||||
|         ), | ||||
|     ), | ||||
|     "DW": ( | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.delayTime", | ||||
|             name="Delay time", | ||||
|             icon="mdi:timer-plus", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.waterHard", | ||||
|             name="Water hard", | ||||
|             icon="mdi:water", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -143,11 +128,11 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         else: | ||||
|             coordinator = HonCoordinator(hass, device) | ||||
|             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator | ||||
|         await coordinator.async_config_entry_first_refresh() | ||||
|  | ||||
|         if descriptions := NUMBERS.get(device.appliance_type): | ||||
|  | ||||
| @ -4,7 +4,7 @@ import logging | ||||
|  | ||||
| from pyhon import Hon | ||||
| from pyhon.appliance import HonAppliance | ||||
| from pyhon.parameter.fixed import HonParameterFixed | ||||
| from pyhon.parameter import HonParameterFixed | ||||
|  | ||||
| from homeassistant.components.select import SelectEntity, SelectEntityDescription | ||||
| from homeassistant.config_entries import ConfigEntry | ||||
| @ -76,7 +76,7 @@ SELECTS = { | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|     ), | ||||
|     "IH": ( | ||||
|     "IV": ( | ||||
|         SelectEntityDescription( | ||||
|             key="startProgram.program", | ||||
|             name="Program", | ||||
| @ -84,14 +84,6 @@ SELECTS = { | ||||
|             translation_key="programs", | ||||
|         ), | ||||
|     ), | ||||
|     "DW": ( | ||||
|         SelectEntityDescription( | ||||
|             key="startProgram.program", | ||||
|             name="Program", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             translation_key="programs_dw", | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -100,11 +92,11 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         else: | ||||
|             coordinator = HonCoordinator(hass, device) | ||||
|             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator | ||||
|         await coordinator.async_config_entry_first_refresh() | ||||
|  | ||||
|         if descriptions := SELECTS.get(device.appliance_type): | ||||
|  | ||||
| @ -21,7 +21,6 @@ from homeassistant.const import ( | ||||
| from homeassistant.core import callback | ||||
| from homeassistant.helpers.entity import EntityCategory | ||||
| from homeassistant.helpers.typing import StateType | ||||
| from homeassistant.const import PERCENTAGE | ||||
|  | ||||
| from .const import DOMAIN | ||||
| from .hon import HonCoordinator, HonEntity | ||||
| @ -222,7 +221,7 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             icon="mdi:thermometer", | ||||
|         ), | ||||
|     ), | ||||
|     "IH": ( | ||||
|     "IV": ( | ||||
|         SensorEntityDescription( | ||||
|             key="remainingTimeMM", | ||||
|             name="Remaining Time", | ||||
| @ -238,69 +237,6 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|         ), | ||||
|         SensorEntityDescription(key="errors", name="Error", icon="mdi:math-log"), | ||||
|     ), | ||||
|     "DW": ( | ||||
|         SensorEntityDescription( | ||||
|             key="startProgram.ecoIndex", | ||||
|             name="Eco Index", | ||||
|             icon="mdi:sprout", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="startProgram.waterEfficiency", | ||||
|             name="Water Efficiency", | ||||
|             icon="mdi:water", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="startProgram.waterSaving", | ||||
|             name="Water Saving", | ||||
|             icon="mdi:water-percent", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             native_unit_of_measurement=PERCENTAGE, | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="startProgram.temp", | ||||
|             name="Temperature", | ||||
|             icon="mdi:thermometer", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="startProgram.energyLabel", | ||||
|             name="Energy Label", | ||||
|             icon="mdi:lightning-bolt-circle", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="startProgram.remainingTime", | ||||
|             name="Time", | ||||
|             icon="mdi:timer", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="machMode", | ||||
|             name="Machine Status", | ||||
|             icon="mdi:information", | ||||
|             translation_key="mode_dw", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="errors", name="Error", icon="mdi:math-log", translation_key="errors" | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="remainingTimeMM", | ||||
|             name="Remaining Time", | ||||
|             icon="mdi:timer", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -309,11 +245,11 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         else: | ||||
|             coordinator = HonCoordinator(hass, device) | ||||
|             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator | ||||
|         await coordinator.async_config_entry_first_refresh() | ||||
|  | ||||
|         if descriptions := SENSORS.get(device.appliance_type): | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import logging | ||||
|  | ||||
| from dataclasses import dataclass | ||||
| from typing import Any | ||||
|  | ||||
| @ -7,7 +8,6 @@ from homeassistant.config_entries import ConfigEntry | ||||
| from homeassistant.const import EntityCategory | ||||
| from pyhon import Hon | ||||
| from pyhon.appliance import HonAppliance | ||||
| from pyhon.parameter.range import HonParameterRange | ||||
|  | ||||
| from .const import DOMAIN | ||||
| from .hon import HonCoordinator, HonEntity | ||||
| @ -89,51 +89,6 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = { | ||||
|             turn_off_key="resumeProgram", | ||||
|         ), | ||||
|     ), | ||||
|     "DW": ( | ||||
|         HonSwitchEntityDescription( | ||||
|             key="active", | ||||
|             name="Dish Washer", | ||||
|             icon="mdi:dishwasher", | ||||
|             turn_on_key="startProgram", | ||||
|             turn_off_key="stopProgram", | ||||
|         ), | ||||
|         HonSwitchEntityDescription( | ||||
|             key="startProgram.extraDry", | ||||
|             name="Extra Dry", | ||||
|             icon="mdi:hair-dryer", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         HonSwitchEntityDescription( | ||||
|             key="startProgram.halfLoad", | ||||
|             name="Half Load", | ||||
|             icon="mdi:fraction-one-half", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         HonSwitchEntityDescription( | ||||
|             key="startProgram.openDoor", | ||||
|             name="Open Door", | ||||
|             icon="mdi:door-open", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         HonSwitchEntityDescription( | ||||
|             key="startProgram.threeInOne", | ||||
|             name="Three in One", | ||||
|             icon="mdi:numeric-3-box-outline", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         HonSwitchEntityDescription( | ||||
|             key="startProgram.ecoExpress", | ||||
|             name="Eco Express", | ||||
|             icon="mdi:sprout", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         HonSwitchEntityDescription( | ||||
|             key="startProgram.addDish", | ||||
|             name="Add Dish", | ||||
|             icon="mdi:silverware-fork-knife", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -142,11 +97,11 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         else: | ||||
|             coordinator = HonCoordinator(hass, device) | ||||
|             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator | ||||
|         await coordinator.async_config_entry_first_refresh() | ||||
|  | ||||
|         if descriptions := SWITCHES.get(device.appliance_type): | ||||
| @ -183,6 +138,13 @@ class HonSwitchEntity(HonEntity, SwitchEntity): | ||||
|         self.entity_description = description | ||||
|         self._attr_unique_id = f"{super().unique_id}{description.key}" | ||||
|  | ||||
|     def available(self) -> bool: | ||||
|         if self.entity_category == EntityCategory.CONFIG: | ||||
|             return ( | ||||
|                 self._device.settings[self.entity_description.key].typology != "fixed" | ||||
|             ) | ||||
|         return True | ||||
|  | ||||
|     @property | ||||
|     def is_on(self) -> bool | None: | ||||
|         """Return True if entity is on.""" | ||||
| @ -198,9 +160,7 @@ class HonSwitchEntity(HonEntity, SwitchEntity): | ||||
|     async def async_turn_on(self, **kwargs: Any) -> None: | ||||
|         if self.entity_category == EntityCategory.CONFIG: | ||||
|             setting = self._device.settings[self.entity_description.key] | ||||
|             setting.value = ( | ||||
|                 setting.max if isinstance(setting, HonParameterRange) else "1" | ||||
|             ) | ||||
|             setting.value = setting.max | ||||
|             self.async_write_ha_state() | ||||
|         else: | ||||
|             await self._device.commands[self.entity_description.turn_on_key].send() | ||||
| @ -208,9 +168,7 @@ class HonSwitchEntity(HonEntity, SwitchEntity): | ||||
|     async def async_turn_off(self, **kwargs: Any) -> None: | ||||
|         if self.entity_category == EntityCategory.CONFIG: | ||||
|             setting = self._device.settings[self.entity_description.key] | ||||
|             setting.value = ( | ||||
|                 setting.min if isinstance(setting, HonParameterRange) else "0" | ||||
|             ) | ||||
|             setting.value = setting.min | ||||
|             self.async_write_ha_state() | ||||
|         else: | ||||
|             await self._device.commands[self.entity_description.turn_off_key].send() | ||||
|  | ||||
| @ -72,16 +72,6 @@ | ||||
|           "13": "Ready to Store H-2", | ||||
|           "14": "Extra Dry H-3" | ||||
|         } | ||||
|       }, | ||||
|       "mode_dw": { | ||||
|         "state": { | ||||
|           "0": "Disconnected", | ||||
|           "1": "Ready", | ||||
|           "2": "Running", | ||||
|           "3": "Delayed start", | ||||
|           "5": "Delayed start cancelled", | ||||
|           "7": "Finished" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "select": { | ||||
| @ -469,113 +459,7 @@ | ||||
|           "iot_standard_frying": "Frying", | ||||
|           "iot_standard_keep_warm": "Keep Warm", | ||||
|           "iot_standard_melting": "Melting", | ||||
|           "iot_standard_simmering": "Simmering" | ||||
|         } | ||||
|       }, | ||||
|       "programs_dw": { | ||||
|         "state": { | ||||
|           "59_min": "Rapid 59'", | ||||
|           "auto_care": "Auto Care", | ||||
|           "auto_care_soil": "Auto Care", | ||||
|           "auto_hygiene": "Auto Hygiene", | ||||
|           "auto_plus": "AutoPlus", | ||||
|           "auto_rapid": "Auto Rapid", | ||||
|           "auto_sensor": "Auto Sensor", | ||||
|           "auto_sensor_soil": "Auto Sensor", | ||||
|           "auto_universal": "Auto Universal 50 - 60°C", | ||||
|           "auto_universal_plus": "Auto Universal+ 65 - 75°C", | ||||
|           "auto_universal_plus_soil": "Auto Universal+ 65 - 75°C", | ||||
|           "auto_universal_soil": "Auto Universal 50 - 60°C", | ||||
|           "auto_wash": "Auto Wash", | ||||
|           "auto_wash_soil": "Auto Wash", | ||||
|           "classe_a_59": "A Wash 59' 65°C", | ||||
|           "delicate": "Delicate 45°C", | ||||
|           "dishwasher_care": "Limescale cleaning", | ||||
|           "eco": "Eco", | ||||
|           "eco_asynch": "Eco 45°C", | ||||
|           "eco_bldc": "Eco 45°C", | ||||
|           "eco_synch": "Eco 45°C", | ||||
|           "gentle_wash": "Gentle wash", | ||||
|           "glass": "Glass", | ||||
|           "glassware": "Glassware 45°C", | ||||
|           "glass_care": "Glass Care", | ||||
|           "hygiene": "Hygiene", | ||||
|           "hygiene_plus": "Hygiene+ 75°C", | ||||
|           "intensive": "Intensive", | ||||
|           "intensive_rapid": "Intensive Rapid", | ||||
|           "iot_auto_sensor": "Auto Sensor", | ||||
|           "iot_auto_universal_soil": "Auto Universal 50 - 60°C", | ||||
|           "iot_auto_wash_soil": "Auto Wash", | ||||
|           "iot_baby_care": "Baby Care", | ||||
|           "iot_breakfast": "Breakfast", | ||||
|           "iot_checkup": "Check-Up", | ||||
|           "iot_china_crystals": "China Crystals", | ||||
|           "iot_classe_a_59": "Rapid 59'", | ||||
|           "iot_cocktail_glasses": "Coktail Glasses", | ||||
|           "iot_cocktail_glasses_soil": "Coktail Glasses", | ||||
|           "iot_daily_care": "Daily Care", | ||||
|           "iot_daily_care_soil": "Daily Care", | ||||
|           "iot_delicate": "Delicate 45°C", | ||||
|           "iot_dinner_for_two": "Dinner for 2", | ||||
|           "iot_dinner_for_two_soil": "Dinner for 2", | ||||
|           "iot_dreft_quick_cycle": "Dreft Quick", | ||||
|           "iot_eco_asynch": "Eco 45°C", | ||||
|           "iot_eco_bldc": "Eco 45°C", | ||||
|           "iot_eco_synch": "Eco 45°C", | ||||
|           "iot_extra_hygiene": "Extra Hygiene", | ||||
|           "iot_fairy_quick_cycle": "Fairy Short", | ||||
|           "iot_happy_hour": "Happy Hour", | ||||
|           "iot_jar_quick_cycle": "Jar Quick", | ||||
|           "iot_party": "Party", | ||||
|           "iot_party_soil": "Party", | ||||
|           "iot_pizza_menu": "Pizza Menu", | ||||
|           "iot_pizza_menu_soil": "Pizza Menu", | ||||
|           "iot_plastic_tupperware": "Plastic & Tupperware", | ||||
|           "iot_porcelain": "Porcelain", | ||||
|           "iot_pot_and_pans": "Pot & Pans", | ||||
|           "iot_pot_and_pans_soil": "Pot & Pans", | ||||
|           "iot_power_mix_wash": "Power Mix Wash", | ||||
|           "iot_power_mix_wash_soil": "Power Mix Wash", | ||||
|           "iot_prewash": "Pre-wash", | ||||
|           "iot_pyrex_and_glassware": "Pyrex & Glassware", | ||||
|           "iot_rapid_29": "Rapid 29'", | ||||
|           "iot_rapid_39": "Rapid 39' 60°C", | ||||
|           "iot_single": "Single", | ||||
|           "iot_steam": "Steam 75°C", | ||||
|           "iot_super_flash": "Super Flash", | ||||
|           "iot_super_wash": "Super Wash", | ||||
|           "iot_turbopower": "TurboPower", | ||||
|           "iot_universal": "Universal 60°C", | ||||
|           "iot_wok_grids_maxi_pans": "Special Pans (Wok, Grids & Maxi Pans)", | ||||
|           "iot_wok_grids_maxi_pans_soil": "Special Pans (Wok, Grids & Maxi Pans)", | ||||
|           "iot_yes_quick_cycle": "Yes Quick", | ||||
|           "night": "Night 55°C", | ||||
|           "prewash": "Pre-wash", | ||||
|           "rapid_20": "Rapid 20'", | ||||
|           "rapid_24": "Rapid 24'", | ||||
|           "rapid_29": "Rapid 29' 50°C", | ||||
|           "rapid_35": "Wash&Dry 35'", | ||||
|           "rapid_39": "Rapid 39' 60°C", | ||||
|           "rapid_49": "Rapid 49'", | ||||
|           "rapid_59": "Rapid 59'", | ||||
|           "sanitising": "Sanitising", | ||||
|           "silence": "Silence", | ||||
|           "silent": "Silent", | ||||
|           "silent_care": "Silent Care", | ||||
|           "smart_ai": "Smart AI", | ||||
|           "smart_ai_pro": "Smart AI Pro", | ||||
|           "smart_ai_rapid": "Smart AI Rapid", | ||||
|           "special": "Special", | ||||
|           "special_pw_prz": "Special", | ||||
|           "steam": "Steam 75°C", | ||||
|           "steam_plus": "Steam Plus 75°C", | ||||
|           "total_care": "Total Care 50°C", | ||||
|           "ultra_silence": "Ultra Silence 55°C", | ||||
|           "ultra_silent": "Ultra Silent 55°C", | ||||
|           "universal": "Universal 60°C", | ||||
|           "universal_plus": "Universal Plus 70°C", | ||||
|           "zone_wash": "Flex Zone Wash", | ||||
|           "zoom_39": "Zoom 39 min" | ||||
|           "iot_standard_simmering": "Simmering", | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
							
								
								
									
										14
									
								
								info.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								info.md
									
									
									
									
									
								
							| @ -5,12 +5,11 @@ | ||||
| Support for home appliances of Haier's mobile app hOn.  | ||||
|  | ||||
| ## Supported Appliances | ||||
| - [Washing Machine](https://github.com/Andre0512/hon#washing-machine) | ||||
| - [Tumble Dryer](https://github.com/Andre0512/hon#tumble-dryer) | ||||
| - [Washer Dryer](https://github.com/Andre0512/hon#washer-dryer) | ||||
| - [Oven](https://github.com/Andre0512/hon#oven) | ||||
| - [Hob](https://github.com/Andre0512/hon#hob) | ||||
| - [Dish Washer](https://github.com/Andre0512/hon#dish-washer) | ||||
| - Tumble Dryer | ||||
| - Washer Dryer | ||||
| - Washing Machine | ||||
| - Oven | ||||
| - Hob | ||||
|  | ||||
| ## Tested Appliances | ||||
| - Haier WD90-B14TEAM5 | ||||
| @ -18,6 +17,7 @@ Support for home appliances of Haier's mobile app hOn. | ||||
| - Haier HWO60SM2F3XH | ||||
| - Hoover H-WASH 500 | ||||
|  | ||||
|  | ||||
| ## Configuration | ||||
|  | ||||
| **Method 1**: [](https://my.home-assistant.io/redirect/config_flow_start/?domain=hon) | ||||
| @ -25,11 +25,13 @@ Support for home appliances of Haier's mobile app 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._ | ||||
|  | ||||
|  | ||||
| ## 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 | ||||
|  | ||||
| * [GitHub repository](https://github.com/Andre0512/hon) (please add a star if you like this integration!) | ||||
| * [pyhOn library](https://github.com/Andre0512/pyhOn) | ||||
| * [Release notes](https://github.com/Andre0512/hon/releases) | ||||
|  | ||||
| @ -1,56 +0,0 @@ | ||||
| from custom_components.hon.binary_sensor import BINARY_SENSORS | ||||
| from custom_components.hon.button import BUTTONS | ||||
| from custom_components.hon.number import NUMBERS | ||||
| from custom_components.hon.select import SELECTS | ||||
| from custom_components.hon.sensor import SENSORS | ||||
| from custom_components.hon.switch import SWITCHES | ||||
|  | ||||
| APPLIANCES = { | ||||
|     "AC": "Air conditioner", | ||||
|     "AP": "Air purifier", | ||||
|     "AS": "Air scanner", | ||||
|     "DW": "Dish washer", | ||||
|     "HO": "Hood", | ||||
|     "IH": "Hob", | ||||
|     "MW": "Microwave", | ||||
|     "OV": "Oven", | ||||
|     "REF": "Fridge", | ||||
|     "RVC": "Robot vacuum cleaner", | ||||
|     "TD": "Tumble dryer", | ||||
|     "WC": "Wine Cellar", | ||||
|     "WD": "Washer dryer", | ||||
|     "WH": "Water Heater", | ||||
|     "WM": "Washing machine", | ||||
| } | ||||
|  | ||||
| ENTITY_CATEGORY_SORT = ["control", "config", "sensor"] | ||||
|  | ||||
| entities = { | ||||
|     "binary_sensor": BINARY_SENSORS, | ||||
|     "button": BUTTONS, | ||||
|     "number": NUMBERS, | ||||
|     "select": SELECTS, | ||||
|     "sensor": SENSORS, | ||||
|     "switch": SWITCHES, | ||||
| } | ||||
|  | ||||
| result = {} | ||||
| for entity_type, appliances in entities.items(): | ||||
|     for appliance, data in appliances.items(): | ||||
|         for entity in data: | ||||
|             attributes = (entity.key, entity.name, entity.icon, entity_type) | ||||
|             category = "control" if entity_type in ["switch", "button"] else "sensor" | ||||
|             result.setdefault(appliance, {}).setdefault( | ||||
|                 entity.entity_category or category, [] | ||||
|             ).append(attributes) | ||||
|  | ||||
| for appliance, categories in sorted(result.items()): | ||||
|     print(f"### {APPLIANCES[appliance]}") | ||||
|     categories = {k: categories[k] for k in ENTITY_CATEGORY_SORT if k in categories} | ||||
|     for category, data in categories.items(): | ||||
|         print(f"#### {str(category).capitalize()}s") | ||||
|         print("| Name | Icon | Entity | Key |") | ||||
|         print("| --- | --- | --- | --- |") | ||||
|         for key, name, icon, entity_type in sorted(data, key=lambda d: d[1]): | ||||
|             icon = f"`{icon}`" if icon else "" | ||||
|             print(f"| {name} | {icon} | `{entity_type}` | `{key}` |") | ||||
		Reference in New Issue
	
	Block a user
	