diff --git a/README.md b/README.md index d5f9d94..7c7c069 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Custom Home Assistant component for controlling and monitoring PetKit devices an - [Fresh Element Mini Pro](https://www.amazon.com/PETKIT-Automatic-Stainless-Indicator-Dispenser-2-8L/dp/B08GS1CPHH/) - [Eversweet 3 Pro Water Fountain](https://www.amazon.com/PETKIT-Wireless-Fountain-Stainless-Dispenser/dp/B09QRH6L3M/) - [Pura X Litter Box](https://www.amazon.com/PETKIT-Self-Cleaning-Scooping-Automatic-Multiple/dp/B08T9CCP1M) +- [Pura MAX Litter Box with/without Pura Air deodorizer](https://www.amazon.com/PETKIT-Self-Cleaning-Capacity-Multiple-Automatic/dp/B09KC7Q4YF) #### Bluetooth only devices that don't use PetKit's BLE relay such as trackers (i.e., PetKit Fit) will not be supported: syncing new data from a bluetooth tracker requires the PetKit mobile app to communicate with the tracker which is not possible when your PetKit account is already in use with this integration. @@ -95,8 +96,9 @@ ___
Fresh Element Infinity (click to expand) - +
Each Feeder has the following entities: +
| Entity | Entity Type | Additional Comments | |-----------------------------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -132,8 +134,9 @@ Each Feeder has the following entities:
Fresh Element Solo (click to expand) - +
Each Feeder has the following entities: +
| Entity | Entity Type | Additional Comments | |-----------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -161,8 +164,9 @@ Each Feeder has the following entities:
Fresh Element Mini Pro (click to expand) - +
Each Feeder has the following entities: +
| Entity | Entity Type | Additional Comments | |-----------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -186,8 +190,9 @@ ___
Eversweet 3 Pro (click to expand) - +
Each water fountain has the following entities: +
| Entity | Entity Type | Additional Comments | |------------------------|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -216,8 +221,9 @@ ___
Pura X (click to expand) - +
Each litter box has the following entities: +
| Entity | Entity Type | Additional Comments | |------------------------|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -247,13 +253,265 @@ Each litter box has the following entities: | `Periodic odor removal` | `Switch` | Only available if litter box is online (Connected to PetKit's servers). | | `Deodorizer level` | `Sensor` | Percent of deodorizer remaining. | | `Error` | `Sensor` | Any errors being reported by the litter box. | -| `Last event` | `Sensor` | - Last event that occured in the litter box.
- sub_events attribute is used to list any events that are associated with the main event.
- This sensor is used to mimic the timeline that is seen in the PetKit app. | +| `Last event` | `Sensor` | - Last event that occured in the litter box.
- sub_events attribute is used to list any events that are associated with the main event.
- This sensor is used to mimic the timeline that is seen in the PetKit app.
- Please see "Last event states" section below | | `Litter level` | `Sensor` | Percent of litter that is left in the litter box. | | `Litter weight` | `Sensor` | - Weight of litter currently in litter box.
- By default this is in Kg. The unit can be changed in the settings of this entity. | | `Manually paused` | `Binary Sensor` | Indicates if the litter box has been manually paused or not. Please see note below table for additional information. | | `RSSI` | `Sensor` | WiFi connection strength. | > When manually pausing the litter box, the manually paused sensor entity will show a state of On. If cleaning isn't resumed, the litter box will (by default) resume cleaning after 10 minutes - the manually paused entity will return to a state of Off. If you manually pause a cleaning and restart Home Assistant while the litter box is paused, this sensor will have an incorrect state of Off. This is a limitation on PetKit's end as the state of the litter box is not reported by their servers. This behavior is evident when a cleaning is paused from the PetKit app, the app is force closed, and opened again. The goal of this entity is to be able to manually keep track of the state of the litter box since PetKit doesn't do that. +
+ Last event states (click to expand) + +
+ The following are all possible event/sub event states and their full descriptions: +
+ +| Event/Sub event state | Full Description | +|-------------------------------------|------------------------------------------------------------------------------------------------------| +| no_events_yet | No events yet | +| event_type_unknown | Event type unknown | +| cleaning_completed | Cleaning completed | +| dumping_over | Dumping Over | +| reset_over | Reset over | +| spray_over | Spray over | +| pet_out | Pet out | +| auto_cleaning_completed | Auto cleaning completed | +| periodic_cleaning_completed | Periodic cleaning completed | +| manual_cleaning_completed | Manual cleaning completed | +| auto_cleaning_terminated | Automatic cleaning terminated | +| periodic_cleaning_terminated | Periodic cleaning terminated | +| manual_cleaning_terminated | Manual cleaning terminated | +| auto_cleaning_failed_full | Automatic cleaning failed, waste collection bin is full, please empty promptly | +| auto_cleaning_failed_hall_l | Automatic cleaning failure, the cylinder is not properly locked in place, please check | +| auto_cleaning_failed_hall_t | Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check | +| scheduled_cleaning_failed_full | Scheduled cleaning failed, waste collection bin is full, please empty promptly | +| scheduled_cleaning_failed_hall_l | Scheduled cleaning failure, the cylinder is not properly locked in place, please check | +| scheduled_cleaning_failed_hall_t | Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check | +| manual_cleaning_failed_full | Manual cleaning failed, waste collection bin is full, please empty promptly | +| manual_cleaning_failed_hall_l | Manual cleaning failure, the cylinder is not properly locked in place, please check | +| manual_cleaning_failed_hall_t | Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check | +| auto_cleaning_canceled | Automatic cleaning canceled, device in operation | +| periodic_cleaning_canceled | Periodic cleaning canceled, device in operation | +| manual_cleaning_canceled | Manual cleaning canceled, device in operation | +| auto_cleaning_canceled_kitten | Kitten mode is enabled, auto cleaning is canceled | +| periodic_cleaning_canceled_kitten | Kitten mode is enabled, periodically cleaning is canceled | +| litter_empty_completed | Cat litter empty completed | +| litter_empty_terminated | Cat litter empty terminated | +| litter_empty_failed_full | Cat litter empty failed, waste collection bin is full, please empty promptly | +| litter_empty_failed_hall_l | Cat litter empty failure, the cylinder is not properly locked in place, please check | +| litter_empty_failed_hall_t | Cat litter empty failure, the litter box's cupper cover is not placed properly, please check | +| reset_completed | Device reset completed | +| reset_terminated | Device reset terminated | +| reset_failed_full | Device reset failed, waste collection bin is full, please empty promptly | +| reset_failed_hall_l | Device reset failure, the cylinder is not properly locked in place, please check | +| reset_failed_hall_t | Device reset failure, the litter box's cupper cover is not placed properly, please check | +| deodorant_finished | Deodorant finished | +| periodic_odor_completed | Periodic odor removal completed | +| manual_odor_completed | Manual odor removal completed | +| deodorant_finished_liquid_lack | Deodorant finished, not enough purifying liquid, please refill in time | +| periodic_odor_completed_liquid_lack | Periodic odor removal completed, not enough purifying liquid, please refill in time | +| manual_odor_completed_liquid_lack | Manual odor removal completed, not enough purifying liquid, please refill in time | +| auto_odor_failed | Automatic odor removal failed, odor eliminator error | +| periodic_odor_failed | Periodic odor removal failure, odor eliminator malfunction | +| manual_odor_failed | Manual odor removal failure, odor eliminator malfunction | +| no_sub_events | No associated sub events | + + +
+
+ +
+ Pura MAX (click to expand) + +
+Each litter box has the following entities: +
+ +| Entity | Entity Type | Additional Comments | +|------------------------|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Dump litter` | `Button` |
- Only available if litter box is online (Connected to PetKit's servers). | +| `Exit maintenance mode` | `Button` |
- Only available if litter box is online (Connected to PetKit's servers). | +| `Odor removal` | `Button` | - Activates immediate odor removal
- Only available if litter box is online (Connected to PetKit's servers). | +| `Pause cleaning` | `Button` | Only available if litter box is online (Connected to PetKit's servers). | +| `Pause exiting maintenance mode` | `Button` | Only available if litter box is online (Connected to PetKit's servers). | +| `Pause litter dumping` | `Button` | Only available if litter box is online (Connected to PetKit's servers). | +| `Power` | `Button` | - Turn litter box on/off
- Only available if litter box is online (Connected to PetKit's servers). | +| `Reset N50 odor eliminator` | `Button` | | +| `Reset Pura Air liquid` | `Button` | Only available if litter box has a Pura Air associated with it | +| `Resume exiting maintenance mode` | `Button` | Only available if litter box is online (Connected to PetKit's servers). | +| `Resume litter dumping` | `Button` | Only available if litter box is online (Connected to PetKit's servers). | +| `Start maintenance mode` | `Button` | Only available if litter box is online (Connected to PetKit's servers). | +| `Start/Resume cleaning` | `Button` | - Start a manual cleaning or resume a cleaning if litter box is currently paused.
- Only available if litter box is online (Connected to PetKit's servers). | +| `Turn light ON` | `Button` | Only available if litter box has a Pura Air associated with it. | +| `Average Use` | `Sensor` | Average use duration of the litter box today. | +| `Last used by` | `Sensor` | Indicates which cat used the litter box last. | +| `Litter` | `Binary Sensor` | Allows for determining if the litter needs to be refilled. | +| `Pura Air liquid` | `Binary Sensor` | - Allows for determining if the Pura Air liquid needs to be refilled.
- Only available if litter box has a Pura Air associated with it | +| `Times used` | `Sensor` | Number of times litter box was used today. | +| `Total use` | `Sensor` | Total number of seconds litter box was used today. | +| `Waste bin` | `Binary Sensor` | Allows for determining if the waste bin is full. | +| `Auto cleaning` | `Switch` | - Only available if litter box is online (Connected to PetKit's servers).
- Not available if Kitten Mode is turned on. | +| `Auto odor removal` | `Switch` | Only available if litter box is online (Connected to PetKit's servers). | +| `Avoid repeat cleaning` | `Switch` | - Only available if litter box is online (Connected to PetKit's servers).
- Not available if Auto Cleaning is disabled.
- Not available if Kitten Mode is turned on. | +| `Child lock`| `Switch` | Only available if litter box is online (Connected to PetKit's servers). | +| `Cleaning delay` | `Number` | - Only available if litter box is online (Connected to PetKit's servers).
- Not available if Auto Cleaning is disabled.
- Not available if Kitten Mode is turned on. | +| `Cleaning interval` | `Select` | - Only available if litter box is online (Connected to PetKit's servers).
- Not available if Auto Cleaning is disabled.
- Not available if Avoid repeat cleaning is disabled.
- Not available if Kitten Mode is turned on. | +| `Continuous rotation` | `Switch` | - Only available if litter box is online (Connected to PetKit's servers). | +| `Deep cleaning` | `Switch` | - Only available if litter box is online (Connected to PetKit's servers).
- Not available if Auto Cleaning is disabled.
- Not available if Avoid repeat cleaning is disabled.
- Not available if Kitten Mode is turned on. | +| `Deep deodorization` | `Switch` | - Only available if litter box is online (Connected to PetKit's servers) and has a Pura Air associated with it.
- Not available if Auto Cleaning is disabled.
- Not available if Avoid repeat cleaning is disabled.
- Not available if Kitten Mode is turned on. | +| `Display` | `Switch` | - Turn display on litter box on or off.
- Only available if litter box is online (Connected to PetKit's servers). | +| `Do not disturb` | `Switch` | Only available if litter box is online (Connected to PetKit's servers). | +| `Kitten mode` | `Switch` | Only available if litter box is online (Connected to PetKit's servers). | +| `Light weight cleaning disabled` | `Switch` | - Only available if litter box is online (Connected to PetKit's servers).
- Not available if Auto Cleaning is disabled.
- Not available if Avoid repeat cleaning is disabled.
- Not available if Kitten Mode is turned on. | +| `Litter type` | `Select` | Type of litter that is being used in the litter box. | +| `Periodic cleaning` | `Switch` | - Only available if litter box is online (Connected to PetKit's servers).
- Not available if Kitten Mode is turned on. | +| `Periodic odor removal` | `Switch` | Only available if litter box is online (Connected to PetKit's servers). | +| `Error` | `Sensor` | Any errors being reported by the litter box. | +| `Last event` | `Sensor` | - Last event that occured in the litter box.
- sub_events attribute is used to list any events that are associated with the main event.
- This sensor is used to mimic the timeline that is seen in the PetKit app.
- Please see "Last event states" section below | +| `Litter level` | `Sensor` | Percent of litter that is left in the litter box. | +| `Litter weight` | `Sensor` | - Weight of litter currently in litter box.
- By default this is in Kg. The unit can be changed in the settings of this entity. | +| `N50 odor eliminator` | `Sensor` | - Amount of days left before N50 filter needs to be replaced. | +| `Pura Air battery` | `Sensor` | - Only available if litter box has a Pura Air associated with it | +| `Pura Air liquid` | `Sensor` | - Only available if litter box has a Pura Air associated with it.
- Amount of liquid left in the Pura Air | +| `RSSI` | `Sensor` | WiFi connection strength. | +| `State` | `Sensor` | - Current work state of the litter box.
- Please see "State entity states" section below. | + + +
+ Last event states (click to expand) + +
+ The following are all possible event/sub event states and their full descriptions: +
+ +| Event/Sub event state | Full Description | +|------------------------------------- |------------------------------------------------------------------------------------------------------| +| no_events_yet | No events yet | +| event_type_unknown | Event type unknown | +| cleaning_completed | Cleaning completed | +| dumping_over | Dumping Over | +| reset_over | Reset over | +| spray_over | Spray over | +| light_over | Light over | +| pet_out | Pet out | +| auto_cleaning_completed | Auto cleaning completed | +| periodic_cleaning_completed | Periodic cleaning completed | +| manual_cleaning_completed | Manual cleaning completed | +| auto_cleaning_terminated | Automatic cleaning terminated | +| periodic_cleaning_terminated | Periodic cleaning terminated | +| manual_cleaning_terminated | Manual cleaning terminated | +| auto_cleaning_failed_full | Automatic cleaning failed, waste collection bin is full, please empty promptly | +| auto_cleaning_failed_hall_t | Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check | +| auto_cleaning_failed_falldown | Automatic cleaning failure, the litter box has been knocked down, please check. | +| auto_cleaning_failed_other | Automatic cleaning failure, device malfunction, please check. | +| scheduled_cleaning_failed_full | Scheduled cleaning failed, waste collection bin is full, please empty promptly | +| scheduled_cleaning_failed_hall_t | Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check | +| scheduled_cleaning_failed_falldown | Scheduled cleaning failure, the litter box has been knocked down, please check. | +| scheduled_cleaning_failed_other | Scheduled cleaning failure, device malfunction, please check. | +| manual_cleaning_failed_full | Manual cleaning failed, waste collection bin is full, please empty promptly | +| manual_cleaning_failed_hall_t | Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check | +| manual_cleaning_failed_falldown | Manual cleaning failure, the litter box has been knocked down, please check. | +| manual_cleaning_failed_other | Manual cleaning failure, device malfunction, please check. | +| auto_cleaning_canceled | Automatic cleaning canceled, device in operation | +| periodic_cleaning_canceled | Periodic cleaning canceled, device in operation | +| manual_cleaning_canceled | Manual cleaning canceled, device in operation | +| auto_cleaning_failed_maintenance | Automatic cleaning failed, the device is in maintenance mode | +| periodic_cleaning_failed_maintenance | Periodically cleaning failed, the device is in maintenance mode | +| auto_cleaning_canceled_kitten | Kitten mode is enabled, auto cleaning is canceled | +| periodic_cleaning_canceled_kitten | Kitten mode is enabled, periodically cleaning is canceled | +| litter_empty_completed | Cat litter empty completed | +| litter_empty_terminated | Cat litter empty terminated | +| litter_empty_failed_full | Cat litter empty failed, waste collection bin is full, please empty promptly | +| litter_empty_failed_hall_t | Cat litter empty failure, the litter box's cupper cover is not placed properly, please check | +| litter_empty_failed_falldown | Cat litter empty failure, the litter box has been knocked down, please check | +| litter_empty_failed_other | Cat litter empty failure, device malfunction, please check. | +| reset_completed | Device reset completed | +| reset_terminated | Device reset terminated | +| reset_failed_full | Device reset failed, waste collection bin is full, please empty promptly | +| reset_failed_hall_t | Device reset failure, the litter box's cupper cover is not placed properly, please check | +| reset_failed_falldown | Device reset failure, the litter box has been knocked down, please check. | +| reset_failed_other | Device reset failure, device malfunction, please check. | +| maintenance_mode | Maintenance mode | +| deodorant_finished | Deodorant finished | +| periodic_odor_completed | Periodic odor removal completed | +| manual_odor_completed | Manual odor removal completed | +| auto_odor_terminated | Automatic odor removal has been terminated. | +| periodic_odor_terminated | Periodic odor removal terminated. | +| manual_odor_terminated | Manual odor removal terminated. | +| auto_odor_failed | Automatic odor removal failed, odor eliminator error | +| periodic_odor_failed | Periodic odor removal failure, odor eliminator malfunction | +| manual_odor_failed | Manual odor removal failure, odor eliminator malfunction | +| auto_odor_canceled | Automatic odor removal has been canceled, the device is running. | +| periodic_odor_canceled | Periodic odor removal canceled. Litter Box is working. | +| manual_odor_canceled | Manual odor removal canceled. Litter Box is working. | +| auto_odor_failed_device | Automatic odor removal failed, no smart spray is connected. | +| periodic_odor_failed_device | Periodic odor removal failed. Odor Removal Device disconnected. | +| manual_odor_failed_device | Manual odor removal failed. Odor Removal Device disconnected. | +| auto_odor_failed_batt | Automatic odor removal failed, please confirm that the battery of smart spray is sufficient. | +| periodic_odor_failed_batt | Periodic odor removal failed. Please make sure the Odor Removal Device has sufficient battery. | +| manual_odor_failed_batt | Manual odor removal failed. Please make sure the Odor Removal Device has sufficient battery. | +| auto_odor_failed_low_batt | Automatic odor removal failed, battery is low. | +| periodic_odor_failed_low_batt | Periodic odor removal failed. Odor Removal Device battery low. | +| manual_odor_failed_low_batt | Manual odor removal failed. Odor Removal Device battery low. | +| cat_stopped_odor | Your cat is using the litter box, deodorization has been canceled | +| light_on | The light is ON | +| light_already_on | The light is on. There is no need to turn on again. | +| light_malfunc | Failing to turn on the light. Device malfunction, please check. | +| light_no_device | Failing to turn on the light. Please bind the odor removal device first. | +| light_batt_cap | Failing to turn on the light. Please check the battery capacity of the odor removal device. | +| light_low_batt | Failing to turn on the light. Low battery capacity of odor removal device. | +| no_sub_events | No associated sub events | + +
+ +
+ State entity states (click to expand) + +
+ The following are all possible State entity states and their full descriptions: +
+ +| State | Full Description | +|------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------| +| cleaning_litter_box | Cleaning litter box | +| cleaning_litter_box_paused | Litter box cleaning paused | +| cleaning_paused_pet_entered | Litter box cleaning paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP. | +| cleaning_paused_system_error | Litter box cleaning paused: System error, operation paused | +| cleaning_paused_pet_approach | Litter box cleaning paused: Your cat is approaching, operation paused | +| cleaning_paused_pet_using | Litter box cleaning paused: Your cat is using the device, operation paused | +| resetting_device | Resetting device | +| litter_box_paused | Litter box paused | +| paused_pet_entered | Litter box paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP. | +| paused_system_error | Litter box paused: System error, operation paused | +| paused_pet_approach | Litter box paused: Your cat is approaching, operation paused | +| paused_pet_using | Litter box paused: Your cat is using the device, operation paused | +| dumping_litter | Dumping cat litter | +| dumping_litter_paused | Dumping cat litter paused | +| dumping_paused_pet_entered | Dumping cat litter paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP. | +| dumping_paused_system_error | Dumping cat litter paused: System error, operation paused | +| dumping_paused_pet_approach | Dumping cat litter paused: Your cat is approaching, operation paused | +| dumping_paused_pet_using | Dumping cat litter paused: Your cat is using the device, operation paused | +| resetting | Resetting | +| leveling | Leveling cat litter, please wait. | +| calibrating | Calibrating litter box, please wait. | +| maintenance_mode | In maintenance mode | +| maintenance_paused_pet_entered | Maintenance mode paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP. | +| maintenance_paused_cover | Maintenance mode paused: The top cover is not installed, operation paused | +| maintenance_paused_system_error | Maintenance mode paused: System error, operation paused | +| maintenance_paused_pet_approach | Maintenance mode paused: Your cat is approaching, operation paused | +| maintenance_paused_pet_using | Maintenance mode paused: Your cat is using the device, operation paused | +| maintenance_paused | Maintenance mode paused | +| exit_maintenance | Exiting maintenance mode | +| maintenance_exit_paused_pet_entered | Maintenance mode exiting paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP. | +| maintenance_exit_paused_cover | Maintenance mode exiting paused: The top cover is not installed, operation paused | +| maintenance_exit_paused_system_error | Maintenance mode exiting paused: System error, operation paused | +| maintenance_exit_paused_pet_approach | Maintenance mode exiting paused: Your cat is approaching, operation paused | +| maintenance_exit_paused_pet_using | Maintenance mode exiting paused: Your cat is using the device, operation paused | +| maintenance_exit_paused | Maintenance mode exiting paused | +| idle | Idle | + +
## Pets @@ -279,3 +537,14 @@ ___ | `Last use duration` | `Sensor` | - Amount of time spent in the litter box during last use today.
- Only available if PetKit account has litter box(es).
- If multiple litter boxes, this will display data obtained from the most recent litter box used. | | `Latest weight` | `Sensor` | - Most recent weight measurement obtained during last litter box use today.
- Only available if PetKit account has litter box(es).
- If multiple litter boxes, this will display data obtained from the most recent litter box used.
- By default the unit used is Kg. Unit can be changed in the settings of the entity. |
+ +# Help With Translations + +A simple way of contributing to this integration is by helping translate the entity names and states to other languages. +
+
**If you speak another language, and would like to help:** +1. Inside of `custom_components/petkit/translations`, you will find two files: `en.json` and `hr.json`. +2. Create a new file and title it based on the [639-1 language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) you are creating a translation for in the format `.json`. +3. Copy the contents from the `en.json` file to your newly created file. +4. If you are unsure what needs to be translated from english to the target language, compare the `en.json` file to the `hr.json` to see what was translated. +5. Create a PR with your new language file placed in the `translations` directory. diff --git a/custom_components/petkit/binary_sensor.py b/custom_components/petkit/binary_sensor.py index f93cfd9..80a9c3a 100644 --- a/custom_components/petkit/binary_sensor.py +++ b/custom_components/petkit/binary_sensor.py @@ -27,37 +27,46 @@ async def async_setup_entry( for wf_id, wf_data in coordinator.data.water_fountains.items(): # Water Fountains (W5) - binary_sensors.extend(( - WFWater(coordinator, wf_id), - )) + binary_sensors.append( + WFWater(coordinator, wf_id) + ) for feeder_id, feeder_data in coordinator.data.feeders.items(): # All Feeders - binary_sensors.extend(( - FoodLevel(coordinator, feeder_id), - )) + binary_sensors.append( + FoodLevel(coordinator, feeder_id) + ) # D4 Feeder if feeder_data.type == 'd4': - binary_sensors.extend(( - BatteryInstalled(coordinator, feeder_id), - )) + binary_sensors.append( + BatteryInstalled(coordinator, feeder_id) + ) # D3 Feeder if feeder_data.type == 'd3': - binary_sensors.extend(( - BatteryCharging(coordinator, feeder_id), - )) + binary_sensors.append( + BatteryCharging(coordinator, feeder_id) + ) # Litter boxes for lb_id, lb_data in coordinator.data.litter_boxes.items(): + # Pura X & Pura MAX + if lb_data.type in ['t3', 't4']: + binary_sensors.extend(( + LBBinFull(coordinator, lb_id), + LBLitterLack(coordinator, lb_id), + )) + # Pura X & Pura MAX with Pura Air + if (lb_data.type == 't3') or ('k3Device' in lb_data.device_detail): + binary_sensors.append( + LBDeodorizerLack(coordinator, lb_id) + ) # Pura X - binary_sensors.extend(( - LBBinFull(coordinator, lb_id), - LBLitterLack(coordinator, lb_id), - LBDeodorizerLack(coordinator, lb_id), - LBManuallyPaused(coordinator, lb_id), - )) + if lb_data.type == 't3': + binary_sensors.append( + LBManuallyPaused(coordinator, lb_id) + ) async_add_entities(binary_sensors) @@ -93,18 +102,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_water_level' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Water level" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "water_level" + @property def device_class(self) -> BinarySensorDeviceClass: """Return entity device class.""" @@ -160,18 +169,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_food_level' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Food level" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "food_level" + @property def device_class(self) -> BinarySensorDeviceClass: """Return entity device class.""" @@ -241,18 +250,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_battery_installed' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Battery installed" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "battery_installed" + @property def entity_category(self) -> EntityCategory: """Set category to diagnostic.""" @@ -306,18 +315,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_battery_charging' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Battery" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "battery" + @property def entity_category(self) -> EntityCategory: """Set category to diagnostic.""" @@ -377,18 +386,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_wastebin' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Wastebin" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "wastebin" + @property def icon(self) -> str: """Set icon.""" @@ -439,18 +448,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_litter_lack' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Litter" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "litter" + @property def icon(self) -> str: """Set icon.""" @@ -501,23 +510,33 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_deodorizer_lack' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Deodorizer" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + #Pura Air + if 'k3Device' in self.lb_data.device_detail: + return "pura_air_liquid" + #Pura X + else: + return "deodorizer" + @property def icon(self) -> str: """Set icon.""" - return 'mdi:spray' + #Pura Air + if 'k3Device' in self.lb_data.device_detail: + return 'mdi:cup' + #Pura X + else: + return 'mdi:spray' @property def device_class(self) -> BinarySensorDeviceClass: @@ -531,6 +550,22 @@ def is_on(self) -> bool: return self.lb_data.device_detail['state']['liquidLack'] + @property + def available(self) -> bool: + """Determine if entity is available. + + Return true if there is a Pura Air + device associated or this is a Pura X. + """ + + if self.lb_data.type == 't4': + if 'k3Device' in self.lb_data.device_detail: + return True + else: + return False + else: + return True + class LBManuallyPaused(CoordinatorEntity, BinarySensorEntity): """Representation of if litter box is manually paused by user.""" @@ -563,18 +598,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_manually_paused' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Manually paused" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "manually_paused" + @property def icon(self) -> str: """Set icon.""" diff --git a/custom_components/petkit/button.py b/custom_components/petkit/button.py index 7247187..4bf6d6f 100644 --- a/custom_components/petkit/button.py +++ b/custom_components/petkit/button.py @@ -32,36 +32,59 @@ async def async_setup_entry( for wf_id, wf_data in coordinator.data.water_fountains.items(): # Water Fountains (W5) if wf_data.ble_relay: - buttons.extend(( - WFResetFilter(coordinator, wf_id), - )) + buttons.append( + WFResetFilter(coordinator, wf_id) + ) for feeder_id, feeder_data in coordinator.data.feeders.items(): # All Feeders - buttons.extend(( - ResetDesiccant(coordinator, feeder_id), - )) + buttons.append( + ResetDesiccant(coordinator, feeder_id) + ) # D3 and D4 if feeder_data.type in ['d3', 'd4']: - buttons.extend(( - CancelManualFeed(coordinator, feeder_id), - )) + buttons.append( + CancelManualFeed(coordinator, feeder_id) + ) # D3 if feeder_data.type == 'd3': - buttons.extend(( - CallPet(coordinator, feeder_id), - )) + buttons.append( + CallPet(coordinator, feeder_id) + ) # Litter boxes for lb_id, lb_data in coordinator.data.litter_boxes.items(): - # Pura X - buttons.extend(( - LBStartCleaning(coordinator, lb_id), - LBPauseCleaning(coordinator, lb_id), - LBOdorRemoval(coordinator, lb_id), - )) + # Pura X & Pura MAX + if lb_data.type in ['t3', 't4']: + buttons.extend(( + LBStartCleaning(coordinator, lb_id), + LBPauseCleaning(coordinator, lb_id) + )) + # Pura X & Pura MAX with Pura Air + if (lb_data.type == 't3') or ('k3Device' in lb_data.device_detail): + buttons.extend(( + LBOdorRemoval(coordinator, lb_id), + LBResetDeodorizer(coordinator, lb_id) + )) + # Pura MAX + if lb_data.type == 't4': + buttons.extend(( + N50Reset(coordinator, lb_id), + MAXStartMaint(coordinator, lb_id), + MAXExitMaint(coordinator, lb_id), + MAXPauseExitMaint(coordinator, lb_id), + MAXResumeExitMaint(coordinator, lb_id), + MAXDumpLitter(coordinator, lb_id), + MAXPauseDumping(coordinator, lb_id), + MAXResumeDumping(coordinator, lb_id) + )) + # Pura MAX with Pura Air + if 'k3Device' in lb_data.device_detail: + buttons.append( + MAXLightOn(coordinator, lb_id) + ) async_add_entities(buttons) @@ -97,18 +120,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_reset_filter' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Reset filter" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "reset_filter" + @property def entity_category(self) -> EntityCategory: """Set category to config.""" @@ -132,7 +155,7 @@ async def async_press(self) -> None: """Handle the button press.""" try: - await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.RESETFILTER) + await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.RESET_FILTER) except BluetoothError: raise PetKitBluetoothError(f'Bluetooth connection to {self.wf_data.data["name"]} failed. Please try resetting filter again.') else: @@ -173,18 +196,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_reset_desiccant' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Reset desiccant" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "reset_desiccant" + @property def available(self) -> bool: """Only make available if device is online.""" @@ -241,18 +264,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_cancel_manual_feed' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Cancel manual feed" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "cancel_manual_feed" + @property def available(self) -> bool: """Only make available if device is online.""" @@ -300,18 +323,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_call_pet' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Call pet" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "call_pet" + @property def available(self) -> bool: """Only make available if device is online.""" @@ -359,18 +382,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_start_cleaning' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Start/Resume cleaning" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "start_cleaning" + @property def icon(self) -> str: """Set icon.""" @@ -384,7 +407,7 @@ def available(self) -> bool: lb_online = self.lb_data.device_detail['state']['pim'] == 1 lb_power_on = self.lb_data.device_detail['state']['power'] == 1 - if (lb_online and lb_power_on): + if lb_online and lb_power_on: return True else: return False @@ -392,7 +415,8 @@ def available(self) -> bool: async def async_press(self) -> None: """Handle the button press.""" - await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.STARTCLEAN) + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.START_CLEAN) + await asyncio.sleep(1.5) await self.coordinator.async_request_refresh() @@ -427,18 +451,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_pause_cleaning' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Pause cleaning" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "pause_cleaning" + @property def icon(self) -> str: """Set icon.""" @@ -452,7 +476,7 @@ def available(self) -> bool: lb_online = self.lb_data.device_detail['state']['pim'] == 1 lb_power_on = self.lb_data.device_detail['state']['power'] == 1 - if (lb_online and lb_power_on): + if lb_online and lb_power_on: return True else: return False @@ -460,7 +484,8 @@ def available(self) -> bool: async def async_press(self) -> None: """Handle the button press.""" - await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.PAUSECLEAN) + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.PAUSE_CLEAN) + await asyncio.sleep(1.5) await self.coordinator.async_request_refresh() @@ -496,10 +521,84 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_odor_removal' @property - def name(self) -> str: - """Return name of the entity.""" + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "odor_removal" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:scent' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + # Pura Air deodorizer + if self.lb_data.type == 't4': + if 'k3Device' in self.lb_data.device_detail: + if lb_online and lb_power_on: + return True + else: + return False + else: + return False + # Pura X + else: + if lb_online and lb_power_on: + return True + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.ODOR_REMOVAL) + await asyncio.sleep(1.5) + await self.coordinator.async_request_refresh() + + +class LBResetDeodorizer(CoordinatorEntity, ButtonEntity): + """Representation of litter box deodorizer reset.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" - return "Odor removal" + return str(self.lb_data.id) + '_reset_deodorizer' @property def has_entity_name(self) -> bool: @@ -507,11 +606,111 @@ def has_entity_name(self) -> bool: return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + # Pura MAX + if self.lb_data.type == 't4': + return "reset_pura_air_liquid" + # Pura X + else: + return "reset_deodorizer" + @property def icon(self) -> str: """Set icon.""" - return 'mdi:scent' + # Pura MAX + if self.lb_data.type == 't4': + return 'mdi:cup' + # Pura X + else: + return 'mdi:scent' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + # Pura Air deodorizer + if self.lb_data.type == 't4': + if 'k3Device' in self.lb_data.device_detail: + if lb_online and lb_power_on: + return True + else: + return False + else: + return False + # Pura X + else: + if lb_online and lb_power_on: + return True + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + # Pura MAX + if self.lb_data.type == 't4': + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.RESET_MAX_DEODOR) + # Pura X: + else: + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.RESET_DEODOR) + await self.coordinator.async_request_refresh() + + +class N50Reset(CoordinatorEntity, ButtonEntity): + """Representation of Pura MAX N50 deodorant reset.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_n50_reset' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "n50_reset" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:air-filter' @property def available(self) -> bool: @@ -520,7 +719,8 @@ def available(self) -> bool: lb_online = self.lb_data.device_detail['state']['pim'] == 1 lb_power_on = self.lb_data.device_detail['state']['power'] == 1 - if (lb_online and lb_power_on): + + if lb_online and lb_power_on: return True else: return False @@ -528,12 +728,12 @@ def available(self) -> bool: async def async_press(self) -> None: """Handle the button press.""" - await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.ODORREMOVAL) + await self.coordinator.client.reset_pura_max_deodorizer(self.lb_data) await self.coordinator.async_request_refresh() -class LBResetDeodorizer(CoordinatorEntity, ButtonEntity): - """Representation of litter box deodorizer reset.""" +class MAXLightOn(CoordinatorEntity, ButtonEntity): + """Representation of Pura MAX light button.""" def __init__(self, coordinator, lb_id): super().__init__(coordinator) @@ -561,13 +761,81 @@ def device_info(self) -> dict[str, Any]: def unique_id(self) -> str: """Sets unique ID for this entity.""" - return str(self.lb_data.id) + '_reset_deodorizer' + return str(self.lb_data.id) + '_max_light' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "light_on" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:lightbulb-on' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + + if lb_online and lb_power_on: + # Make sure Pura Air is connected + if 'k3Device' in self.lb_data.device_detail: + return True + else: + return False + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.LIGHT_ON) + await asyncio.sleep(1.5) + await self.coordinator.async_request_refresh() + + +class MAXStartMaint(CoordinatorEntity, ButtonEntity): + """Representation of starting Pura MAX maintenance mode.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } @property - def name(self) -> str: - """Return name of the entity.""" + def unique_id(self) -> str: + """Sets unique ID for this entity.""" - return "Reset deodorizer" + return str(self.lb_data.id) + '_start_max_maint' @property def has_entity_name(self) -> bool: @@ -575,11 +843,17 @@ def has_entity_name(self) -> bool: return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "start_maintenance" + @property def icon(self) -> str: """Set icon.""" - return 'mdi:scent' + return 'mdi:tools' @property def available(self) -> bool: @@ -588,7 +862,428 @@ def available(self) -> bool: lb_online = self.lb_data.device_detail['state']['pim'] == 1 lb_power_on = self.lb_data.device_detail['state']['power'] == 1 - if (lb_online and lb_power_on): + + if lb_online and lb_power_on: + return True + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.START_MAINTENANCE) + await asyncio.sleep(1.5) + await self.coordinator.async_request_refresh() + + +class MAXExitMaint(CoordinatorEntity, ButtonEntity): + """Representation of exiting Pura MAX maintenance mode.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_exit_max_maint' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "exit_maintenance" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:tools' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + + if lb_online and lb_power_on: + return True + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.EXIT_MAINTENANCE) + await asyncio.sleep(1.5) + await self.coordinator.async_request_refresh() + + +class MAXPauseExitMaint(CoordinatorEntity, ButtonEntity): + """Representation of pausing exiting Pura MAX maintenance mode.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_pause_exit_max_maint' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "pause_exit_maintenance" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:tools' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + + if lb_online and lb_power_on: + return True + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.PAUSE_MAINTENANCE_EXIT) + await asyncio.sleep(1.5) + await self.coordinator.async_request_refresh() + + +class MAXResumeExitMaint(CoordinatorEntity, ButtonEntity): + """Representation of continuing exiting Pura MAX maintenance mode.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_resume_exit_max_maint' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "resume_exit_maintenance" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:tools' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + + if lb_online and lb_power_on: + return True + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.RESUME_MAINTENANCE_EXIT) + await asyncio.sleep(1.5) + await self.coordinator.async_request_refresh() + + +class MAXDumpLitter(CoordinatorEntity, ButtonEntity): + """Representation of dumping cat litter.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_dump_litter' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "dump_litter" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:landslide' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + + if lb_online and lb_power_on: + return True + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.DUMP_LITTER) + await asyncio.sleep(1.5) + await self.coordinator.async_request_refresh() + + +class MAXPauseDumping(CoordinatorEntity, ButtonEntity): + """Representation of pausing dumping cat litter.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_pause_dump_litter' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "pause_dump_litter" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:landslide' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + + if lb_online and lb_power_on: + return True + else: + return False + + async def async_press(self) -> None: + """Handle the button press.""" + + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.PAUSE_LITTER_DUMP) + await asyncio.sleep(1.5) + await self.coordinator.async_request_refresh() + + +class MAXResumeDumping(CoordinatorEntity, ButtonEntity): + """Representation of resuming dumping cat litter.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_resume_dump_litter' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "resume_dump_litter" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:landslide' + + @property + def available(self) -> bool: + """Only make available if device is online and on.""" + + lb_online = self.lb_data.device_detail['state']['pim'] == 1 + lb_power_on = self.lb_data.device_detail['state']['power'] == 1 + + + if lb_online and lb_power_on: return True else: return False @@ -596,5 +1291,6 @@ def available(self) -> bool: async def async_press(self) -> None: """Handle the button press.""" - await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.RESETDEODOR) + await self.coordinator.client.control_litter_box(self.lb_data, LitterBoxCommand.RESUME_LITTER_DUMP) + await asyncio.sleep(1.5) await self.coordinator.async_request_refresh() diff --git a/custom_components/petkit/config_flow.py b/custom_components/petkit/config_flow.py index e38c0d3..0b813c3 100644 --- a/custom_components/petkit/config_flow.py +++ b/custom_components/petkit/config_flow.py @@ -4,7 +4,7 @@ from collections.abc import Mapping from typing import Any -from petkitaio.exceptions import AuthError +from petkitaio.exceptions import AuthError, ServerError import voluptuous as vol from homeassistant import config_entries @@ -55,6 +55,8 @@ async def async_step_reauth_confirm( errors["base"] = "cannot_connect" except NoDevicesError: errors["base"] = "no_devices" + except ServerError: + errors["base"] = "server_busy" else: assert self.entry is not None @@ -94,6 +96,8 @@ async def async_step_user( errors["base"] = "cannot_connect" except NoDevicesError: errors["base"] = "no_devices" + except ServerError: + errors["base"] = "server_busy" else: await self.async_set_unique_id(email) self._abort_if_unique_id_configured() diff --git a/custom_components/petkit/const.py b/custom_components/petkit/const.py index d86b9ae..f944840 100644 --- a/custom_components/petkit/const.py +++ b/custom_components/petkit/const.py @@ -35,30 +35,30 @@ # Dictionaries and lists. -LIGHT_BRIGHTNESS_OPTIONS = ['Low', 'Medium', 'High'] +LIGHT_BRIGHTNESS_OPTIONS = ['low', 'medium', 'high'] LIGHT_BRIGHTNESS_COMMAND = { - W5Command.LIGHTLOW: "Low", - W5Command.LIGHTMEDIUM: "Medium", - W5Command.LIGHTHIGH: "High" + W5Command.LIGHT_LOW: "low", + W5Command.LIGHT_MEDIUM: "medium", + W5Command.LIGHT_HIGH: "high" } LIGHT_BRIGHTNESS_NAMED = { - 1: 'Low', - 2: 'Medium', - 3: 'High' + 1: 'low', + 2: 'medium', + 3: 'high' } -WF_MODE_OPTIONS = ['Normal', 'Smart'] +WF_MODE_OPTIONS = ['normal', 'smart'] WF_MODE_NAMED = { - 1: 'Normal', - 2: 'Smart' + 1: 'normal', + 2: 'smart' } WF_MODE_COMMAND = { - W5Command.NORMAL: "Normal", - W5Command.SMART: "Smart" + W5Command.NORMAL: "normal", + W5Command.SMART: "smart" } WATER_FOUNTAINS = { @@ -106,9 +106,9 @@ ] LITTER_TYPE_NAMED = { - 1: 'Bentonite', - 2: 'Tofu', - 3: 'Mixed' + 1: 'bentonite', + 2: 'tofu', + 3: 'mixed' } MANUAL_FEED_NAMED = { @@ -124,104 +124,3 @@ 45: '9/20th Cup (45g)', 50: '1/2 Cup (50g)' } - -VALID_EVENT_TYPES = [5, 6, 7, 8, 10] -EVENT_TYPE_NAMED = { - 5: 'Cleaning Completed', - 6: 'Dumping Over', - 7: 'Reset Over', - 8: 'Spray Over', - 10: 'Pet Out', -} - -# Event Type --> Result --> Reason --> Optional(Error) -EVENT_DESCRIPTION = { - 5: { - 0: { - 0: 'Auto cleaning completed', - 1: 'Periodic cleaning completed', - 2: 'Manual cleaning completed', - 3: 'Manual cleaning completed', - }, - 1: { - 0: 'Automatic cleaning terminated', - 1: 'Periodic cleaning terminated', - 2: 'Manual cleaning terminated', - 3: 'Manual cleaning terminated', - }, - 2: { - 0: { - 'full': 'Automatic cleaning failed, waste collection bin is full, please empty promptly', - 'hallL': 'Automatic cleaning failure, the cylinder is not properly locked in place, please check', - 'hallT': 'Automatic cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', - }, - 1: { - 'full': 'Scheduled cleaning failed, waste collection bin is full, please empty promptly', - 'hallL': 'Scheduled cleaning failure, the cylinder is not properly locked in place, please check', - 'hallT': 'Scheduled cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', - }, - 2: { - 'full': 'Manual cleaning failed, waste collection bin is full, please empty promptly', - 'hallL': 'Manual cleaning failure, the cylinder is not properly locked in place, please check', - 'hallT': 'Manual cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', - }, - 3: { - 'full': 'Manual cleaning failed, waste collection bin is full, please empty promptly', - 'hallL': 'Manual cleaning failure, the cylinder is not properly locked in place, please check', - 'hallT': 'Manual cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', - }, - }, - 3: { - 0: 'Automatic cleaning cancelled, device in operation', - 1: 'Periodic cleaning cancelled, device in operation', - 2: 'Manual cleaning cancelled, device in operation', - 3: 'Manual cleaning cancelled, device in operation', - }, - 4: { - 0: 'Kitten mode is enabled, auto cleaning is canceled', - 1: 'Kitten mode is enabled, periodically cleaning is canceled', - }, - }, - 6: { - 0: 'Cat litter empty completed', - 1: 'Cat litter empty terminated', - 2: { - 'full': 'Cat litter empty failed, waste collection bin is full, please empty promptly', - 'hallL': 'Cat litter empty failure, the cylinder is not properly locked in place, please check', - 'hallT': 'Cat litter empty failure, the litter box\'s cupper cover is not placed properly, please check', - }, - }, - 7: { - 0: 'Device reset completed', - 1: 'Device reset terminated', - 2: { - 'full': 'Device reset failed, waste collection bin is full, please empty promptly', - 'hallL': 'Device reset failure, the cylinder is not properly locked in place, please check', - 'hallT': 'Device reset failure, the litter box\'s cupper cover is not placed properly, please check', - }, - }, - 8: { - 0: { - 0: 'Deodorant finished', - 1: 'Periodic odor removal completed', - 2: 'Manual odor removal completed', - 3: 'Manual odor removal completed', - }, - 1: { - 0: 'Deodorant finished, not enough purifying liquid, please refill in time', - 1: 'Periodic odor removal completed, not enough purifying liquid, please refill in time', - 2: 'Manual odor removal completed, not enough purifying liquid, please refill in time', - 3: 'Manual odor removal completed, not enough purifying liquid, please refill in time', - }, - 2: { - 0: 'Automatic odor removal failed, odor eliminator error', - 1: 'Periodic odor removal failure, odor eliminator malfunction', - 2: 'Manual odor removal failure, odor eliminator malfunction', - 3: 'Manual odor removal failure, odor eliminator malfunction', - }, - }, -} - - - - diff --git a/custom_components/petkit/coordinator.py b/custom_components/petkit/coordinator.py index b4db7b3..4612d8e 100644 --- a/custom_components/petkit/coordinator.py +++ b/custom_components/petkit/coordinator.py @@ -4,7 +4,7 @@ from datetime import timedelta from petkitaio import PetKitClient -from petkitaio.exceptions import AuthError, PetKitError +from petkitaio.exceptions import AuthError, PetKitError, ServerError from petkitaio.model import PetKitData @@ -44,8 +44,9 @@ async def _async_update_data(self) -> PetKitData: try: data = await self.client.get_petkit_data() + LOGGER.debug(f'Found the following PetKit devices/pets: {data}') except AuthError as error: raise ConfigEntryAuthFailed(error) from error - except PetKitError as error: + except (ServerError, PetKitError) as error: raise UpdateFailed(error) from error return data diff --git a/custom_components/petkit/litter_events.py b/custom_components/petkit/litter_events.py new file mode 100644 index 0000000..4588adc --- /dev/null +++ b/custom_components/petkit/litter_events.py @@ -0,0 +1,357 @@ +### Dictionaries describing the events returned by litter box ### + + +# Event Type --> Result --> Reason --> Optional(Error) +EVENT_DESCRIPTION = { + 5: { + 0: { + 0: 'auto_cleaning_completed', + 1: 'periodic_cleaning_completed', + 2: 'manual_cleaning_completed', + 3: 'manual_cleaning_completed', + }, + 1: { + 0: 'auto_cleaning_terminated', + 1: 'periodic_cleaning_terminated', + 2: 'manual_cleaning_terminated', + 3: 'manual_cleaning_terminated', + }, + 2: { + 0: { + 'full': 'auto_cleaning_failed_full', + 'hallL': 'auto_cleaning_failed_hall_l', + 'hallT': 'auto_cleaning_failed_hall_t', + }, + 1: { + 'full': 'scheduled_cleaning_failed_full', + 'hallL': 'scheduled_cleaning_failed_hall_l', + 'hallT': 'scheduled_cleaning_failed_hall_t', + }, + 2: { + 'full': 'manual_cleaning_failed_full', + 'hallL': 'manual_cleaning_failed_hall_l', + 'hallT': 'manual_cleaning_failed_hall_t', + }, + 3: { + 'full': 'manual_cleaning_failed_full', + 'hallL': 'manual_cleaning_failed_hall_l', + 'hallT': 'manual_cleaning_failed_hall_t', + }, + }, + 3: { + 0: 'auto_cleaning_canceled', + 1: 'periodic_cleaning_canceled', + 2: 'manual_cleaning_canceled', + 3: 'manual_cleaning_canceled', + }, + 4: { + 0: 'auto_cleaning_canceled_kitten', + 1: 'periodic_cleaning_canceled_kitten', + }, + }, + 6: { + 0: 'litter_empty_completed', + 1: 'litter_empty_terminated', + 2: { + 'full': 'litter_empty_failed_full', + 'hallL': 'litter_empty_failed_hall_l', + 'hallT': 'litter_empty_failed_hall_t', + }, + }, + 7: { + 0: 'reset_completed', + 1: 'reset_terminated', + 2: { + 'full': 'reset_failed_full', + 'hallL': 'reset_failed_hall_l', + 'hallT': 'reset_failed_hall_t', + }, + }, + 8: { + 0: { + 0: 'deodorant_finished', + 1: 'periodic_odor_completed', + 2: 'manual_odor_completed', + 3: 'manual_odor_completed', + }, + 1: { + 0: 'deodorant_finished_liquid_lack', + 1: 'periodic_odor_completed_liquid_lack', + 2: 'manual_odor_completed_liquid_lack', + 3: 'manual_odor_completed_liquid_lack', + }, + 2: { + 0: 'auto_odor_failed', + 1: 'periodic_odor_failed', + 2: 'manual_odor_failed', + 3: 'manual_odor_failed', + }, + }, +} + +PURA_X_EVENT_FULL_DESCRIPTION = { + 'auto_cleaning_completed': 'Auto cleaning completed', + 'periodic_cleaning_completed': 'Periodic cleaning completed', + 'manual_cleaning_completed': 'Manual cleaning completed', + 'auto_cleaning_terminated': 'Automatic cleaning terminated', + 'periodic_cleaning_terminated': 'Periodic cleaning terminated', + 'manual_cleaning_terminated': 'Manual cleaning terminated', + 'auto_cleaning_failed_full': 'Automatic cleaning failed, waste collection bin is full, please empty promptly', + 'auto_cleaning_failed_hall_l': 'Automatic cleaning failure, the cylinder is not properly locked in place, please check', + 'auto_cleaning_failed_hall_t': 'Automatic cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', + 'scheduled_cleaning_failed_full': 'Scheduled cleaning failed, waste collection bin is full, please empty promptly', + 'scheduled_cleaning_failed_hall_l': 'Scheduled cleaning failure, the cylinder is not properly locked in place, please check', + 'scheduled_cleaning_failed_hall_t': 'Scheduled cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', + 'manual_cleaning_failed_full': 'Manual cleaning failed, waste collection bin is full, please empty promptly', + 'manual_cleaning_failed_hall_l': 'Manual cleaning failure, the cylinder is not properly locked in place, please check', + 'manual_cleaning_failed_hall_t': 'Manual cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', + 'auto_cleaning_canceled': 'Automatic cleaning canceled, device in operation', + 'periodic_cleaning_canceled': 'Periodic cleaning canceled, device in operation', + 'manual_cleaning_canceled': 'Manual cleaning canceled, device in operation', + 'auto_cleaning_canceled_kitten': 'Kitten mode is enabled, auto cleaning is canceled', + 'periodic_cleaning_canceled_kitten': 'Kitten mode is enabled, periodically cleaning is canceled', + 'litter_empty_completed': 'Cat litter empty completed', + 'litter_empty_terminated': 'Cat litter empty terminated', + 'litter_empty_failed_full': 'Cat litter empty failed, waste collection bin is full, please empty promptly', + 'litter_empty_failed_hall_l': 'Cat litter empty failure, the cylinder is not properly locked in place, please check', + 'litter_empty_failed_hall_t': 'Cat litter empty failure, the litter box\'s cupper cover is not placed properly, please check', + 'reset_completed': 'Device reset completed', + 'reset_terminated': 'Device reset terminated', + 'reset_failed_full': 'Device reset failed, waste collection bin is full, please empty promptly', + 'reset_failed_hall_l': 'Device reset failure, the cylinder is not properly locked in place, please check', + 'reset_failed_hall_t': 'Device reset failure, the litter box\'s cupper cover is not placed properly, please check', + 'deodorant_finished': 'Deodorant finished', + 'periodic_odor_completed': 'Periodic odor removal completed', + 'manual_odor_completed': 'Manual odor removal completed', + 'deodorant_finished_liquid_lack': 'Deodorant finished, not enough purifying liquid, please refill in time', + 'periodic_odor_completed_liquid_lack': 'Periodic odor removal completed, not enough purifying liquid, please refill in time', + 'manual_odor_completed_liquid_lack': 'Manual odor removal completed, not enough purifying liquid, please refill in time', + 'auto_odor_failed': 'Automatic odor removal failed, odor eliminator error', + 'periodic_odor_failed': 'Periodic odor removal failure, odor eliminator malfunction', + 'manual_odor_failed': 'Manual odor removal failure, odor eliminator malfunction' +} + +EVENT_TYPE_NAMED = { + 5: 'cleaning_completed', + 6: 'dumping_over', + 7: 'reset_over', + 8: 'spray_over', + 10: 'pet_out', +} +VALID_EVENT_TYPES = [5, 6, 7, 8, 10] +MAX_EVENT_TYPES = [5, 6, 7, 8, 10, 17] + +MAX_EVENT_TYPE_NAMED = { + 5: 'cleaning_completed', + 6: 'dumping_over', + 7: 'reset_over', + 8: 'spray_over', + 10: 'pet_out', + 17: 'light_over' +} + +# Event Type --> Result --> Reason --> Optional(Error) +MAX_EVENT_DESCRIPTION = { + 5: { + 0: { + 0: 'auto_cleaning_completed', + 1: 'periodic_cleaning_completed', + 2: 'manual_cleaning_completed', + 3: 'manual_cleaning_completed', + }, + 1: { + 0: 'auto_cleaning_terminated', + 1: 'periodic_cleaning_terminated', + 2: 'manual_cleaning_terminated', + 3: 'manual_cleaning_terminated', + }, + 2: { + 0: { + 'full': 'auto_cleaning_failed_full', + 'hallT': 'auto_cleaning_failed_hall_t', + 'falldown': 'auto_cleaning_failed_falldown' + }, + 1: { + 'full': 'scheduled_cleaning_failed_full', + 'hallT': 'scheduled_cleaning_failed_hall_t', + 'falldown': 'scheduled_cleaning_failed_falldown' + }, + 2: { + 'full': 'manual_cleaning_failed_full', + 'hallT': 'manual_cleaning_failed_hall_t', + 'falldown': 'manual_cleaning_failed_falldown' + }, + 3: { + 'full': 'manual_cleaning_failed_full', + 'hallT': 'manual_cleaning_failed_hall_t', + 'falldown': 'manual_cleaning_failed_falldown' + }, + }, + 3: { + 0: 'auto_cleaning_canceled', + 1: 'periodic_cleaning_canceled', + 2: 'manual_cleaning_canceled', + 3: 'manual_cleaning_canceled', + }, + 4: { + 0: 'auto_cleaning_failed_full', + 1: 'scheduled_cleaning_failed_full', + 2: 'manual_cleaning_failed_full', + 3: 'manual_cleaning_failed_full', + }, + 5: { + 0: 'auto_cleaning_failed_maintenance', + 1: 'periodic_cleaning_failed_maintenance', + }, + 7: { + 0: 'auto_cleaning_canceled_kitten', + 1: 'periodic_cleaning_canceled_kitten', + } + }, + 6: { + 0: 'litter_empty_completed', + 1: 'litter_empty_terminated', + 2: { + 'full': 'litter_empty_failed_full', + 'hallT': 'litter_empty_failed_hall_t', + 'falldown': 'litter_empty_failed_falldown', + }, + }, + 7: { + 0: 'reset_completed', + 1: 'reset_terminated', + 2: { + 'full': 'reset_failed_full', + 'hallT': 'reset_failed_hall_t', + 'falldown': 'reset_failed_falldown' + }, + 5: 'maintenance_mode' + }, + 8: { + 0: { + 0: 'deodorant_finished', + 1: 'periodic_odor_completed', + 2: 'manual_odor_completed', + 3: 'manual_odor_completed', + }, + 1: { + 0: 'auto_odor_terminated', + 1: 'periodic_odor_terminated', + 2: 'manual_odor_terminated', + 3: 'manual_odor_terminated', + }, + 2: { + 0: 'auto_odor_failed', + 1: 'periodic_odor_failed', + 2: 'manual_odor_failed', + 3: 'manual_odor_failed', + }, + 4: { + 0: 'auto_odor_canceled', + 1: 'periodic_odor_canceled', + 2: 'manual_odor_canceled', + 3: 'manual_odor_canceled' + }, + 5: { + 0: 'auto_odor_failed_device', + 1: 'periodic_odor_failed_device', + 2: 'manual_odor_failed_device', + 3: 'manual_odor_failed_device' + }, + 6: { + 0: 'auto_odor_failed_batt', + 1: 'periodic_odor_failed_batt', + 2: 'manual_odor_failed_batt', + 3: 'manual_odor_failed_batt' + }, + 7: { + 0: 'auto_odor_failed_low_batt', + 1: 'periodic_odor_failed_low_batt', + 2: 'manual_odor_failed_low_batt', + 3: 'manual_odor_failed_low_batt' + }, + 8: { + 0: 'deodorant_finished', + 1: 'periodic_odor_completed', + 2: 'manual_odor_completed' + }, + 9: 'cat_stopped_odor' + }, + 17: { + 0: 'light_on', + 1: 'light_already_on', + 2: 'light_malfunc', + 5: 'light_no_device', + 6: 'light_batt_cap', + 7: 'light_low_batt' + } +} + +PURA_MAX_EVENT_FULL_DESCRIPTION = { + 'auto_cleaning_completed': 'Auto cleaning completed', + 'periodic_cleaning_completed': 'Periodic cleaning completed', + 'manual_cleaning_completed': 'Manual cleaning completed', + 'auto_cleaning_terminated': 'Automatic cleaning terminated', + 'periodic_cleaning_terminated': 'Periodic cleaning terminated', + 'manual_cleaning_terminated': 'Manual cleaning terminated', + 'auto_cleaning_failed_full': 'Automatic cleaning failed, waste collection bin is full, please empty promptly', + 'auto_cleaning_failed_hall_t': 'Automatic cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', + 'auto_cleaning_failed_falldown': 'Automatic cleaning failure, the litter box has been knocked down, please check.', + 'auto_cleaning_failed_other': 'Automatic cleaning failure, device malfunction, please check.', + 'scheduled_cleaning_failed_full': 'Scheduled cleaning failed, waste collection bin is full, please empty promptly', + 'scheduled_cleaning_failed_hall_t': 'Scheduled cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', + 'scheduled_cleaning_failed_falldown': 'Scheduled cleaning failure, the litter box has been knocked down, please check.', + 'scheduled_cleaning_failed_other': 'Scheduled cleaning failure, device malfunction, please check.', + 'manual_cleaning_failed_full': 'Manual cleaning failed, waste collection bin is full, please empty promptly', + 'manual_cleaning_failed_hall_t': 'Manual cleaning failure, the litter box\'s upper cupper cover is not placed properly, please check', + 'manual_cleaning_failed_falldown': 'Manual cleaning failure, the litter box has been knocked down, please check.', + 'manual_cleaning_failed_other': 'Manual cleaning failure, device malfunction, please check.', + 'auto_cleaning_canceled': 'Automatic cleaning canceled, device in operation', + 'periodic_cleaning_canceled': 'Periodic cleaning canceled, device in operation', + 'manual_cleaning_canceled': 'Manual cleaning canceled, device in operation', + 'auto_cleaning_failed_maintenance': 'Automatic cleaning failed, the device is in maintenance mode', + 'periodic_cleaning_failed_maintenance': 'Periodically cleaning failed, the device is in maintenance mode', + 'auto_cleaning_canceled_kitten': 'Kitten mode is enabled, auto cleaning is canceled', + 'periodic_cleaning_canceled_kitten': 'Kitten mode is enabled, periodically cleaning is canceled', + 'litter_empty_completed': 'Cat litter empty completed', + 'litter_empty_terminated': 'Cat litter empty terminated', + 'litter_empty_failed_full': 'Cat litter empty failed, waste collection bin is full, please empty promptly', + 'litter_empty_failed_hall_t': 'Cat litter empty failure, the litter box\'s cupper cover is not placed properly, please check', + 'litter_empty_failed_falldown': 'Cat litter empty failure, the litter box has been knocked down, please check', + 'litter_empty_failed_other': 'Cat litter empty failure, device malfunction, please check.', + 'reset_completed': 'Device reset completed', + 'reset_terminated': 'Device reset terminated', + 'reset_failed_full': 'Device reset failed, waste collection bin is full, please empty promptly', + 'reset_failed_hall_t': 'Device reset failure, the litter box\'s cupper cover is not placed properly, please check', + 'reset_failed_falldown': 'Device reset failure, the litter box has been knocked down, please check.', + 'reset_failed_other': 'Device reset failure, device malfunction, please check.', + 'maintenance_mode': 'Maintenance mode', + 'deodorant_finished': 'Deodorant finished', + 'periodic_odor_completed': 'Periodic odor removal completed', + 'manual_odor_completed': 'Manual odor removal completed', + 'auto_odor_terminated': 'Automatic odor removal has been terminated.', + 'periodic_odor_terminated': 'Periodic odor removal terminated.', + 'manual_odor_terminated': 'Manual odor removal terminated.', + 'auto_odor_failed': 'Automatic odor removal failed, odor eliminator error', + 'periodic_odor_failed': 'Periodic odor removal failure, odor eliminator malfunction', + 'manual_odor_failed': 'Manual odor removal failure, odor eliminator malfunction', + 'auto_odor_canceled': 'Automatic odor removal has been canceled, the device is running.', + 'periodic_odor_canceled': 'Periodic odor removal canceled. Litter Box is working.', + 'manual_odor_canceled': 'Manual odor removal canceled. Litter Box is working.', + 'auto_odor_failed_device': 'Automatic odor removal failed, no smart spray is connected.', + 'periodic_odor_failed_device': 'Periodic odor removal failed. Odor Removal Device disconnected.', + 'manual_odor_failed_device': 'Manual odor removal failed. Odor Removal Device disconnected.', + 'auto_odor_failed_batt': 'Automatic odor removal failed, please confirm that the battery of smart spray is sufficient.', + 'periodic_odor_failed_batt': 'Periodic odor removal failed. Please make sure the Odor Removal Device has sufficient battery.', + 'manual_odor_failed_batt': 'Manual odor removal failed. Please make sure the Odor Removal Device has sufficient battery.', + 'auto_odor_failed_low_batt': 'Automatic odor removal failed, battery is low.', + 'periodic_odor_failed_low_batt': 'Periodic odor removal failed. Odor Removal Device battery low.', + 'manual_odor_failed_low_batt': 'Manual odor removal failed. Odor Removal Device battery low.', + 'cat_stopped_odor': 'Your cat is using the litter box, deodorization has been canceled', + 'light_on': 'The light is ON', + 'light_already_on': 'The light is on. There is no need to turn on again.', + 'light_malfunc': 'Failing to turn on the light. Device malfunction, please check.', + 'light_no_device': 'Failing to turn on the light. Please bind the odor removal device first.', + 'light_batt_cap': 'Failing to turn on the light. Please check the battery capacity of the odor removal device.', + 'light_low_batt': 'Failing to turn on the light. Low battery capacity of odor removal device.' +} diff --git a/custom_components/petkit/manifest.json b/custom_components/petkit/manifest.json index 2143f24..fd0611c 100644 --- a/custom_components/petkit/manifest.json +++ b/custom_components/petkit/manifest.json @@ -8,6 +8,6 @@ "integration_type": "hub", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/RobertD502/home-assistant-petkit/issues", - "requirements": ["petkitaio==0.1.1", "tzlocal>=4.2"], - "version": "0.1.0b2" + "requirements": ["petkitaio==0.1.2", "tzlocal>=4.2"], + "version": "0.1.1" } diff --git a/custom_components/petkit/number.py b/custom_components/petkit/number.py index d4276dc..f0bbc75 100644 --- a/custom_components/petkit/number.py +++ b/custom_components/petkit/number.py @@ -35,9 +35,9 @@ async def async_setup_entry( # Pets for pet_id, pet_data in coordinator.data.pets.items(): - numbers.extend(( - PetWeight(coordinator, pet_id), - )) + numbers.append( + PetWeight(coordinator, pet_id) + ) for feeder_id, feeder_data in coordinator.data.feeders.items(): # Only D3 Feeder @@ -48,12 +48,11 @@ async def async_setup_entry( ManualFeed(coordinator, feeder_id), )) - # Litter boxes for lb_id, lb_data in coordinator.data.litter_boxes.items(): - # Pura X - numbers.extend(( - LBCleaningDelay(coordinator, lb_id), - )) + # Pura X & Pura MAX + numbers.append( + LBCleaningDelay(coordinator, lb_id) + ) async_add_entities(numbers) @@ -88,18 +87,18 @@ def unique_id(self) -> str: return self.pet_data.id + '_set_weight' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Set weight" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "set_weight" + @property def entity_picture(self) -> str: """Grab associated pet picture.""" @@ -120,7 +119,7 @@ def icon(self) -> str: @property def native_value(self) -> float: - """Returns current weight.""" + """Returns current weight.""" pet_weight = self.pet_data.data['weight'] if self.hass.config.units is METRIC_SYSTEM: @@ -216,18 +215,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_surplus' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Surplus" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "surplus" + @property def icon(self) -> str: """Set icon.""" @@ -242,7 +241,7 @@ def entity_category(self) -> EntityCategory: @property def native_value(self) -> int: - """Returns current surplus setting.""" + """Returns current surplus setting.""" return self.feeder_data.data['settings']['surplus'] @@ -331,18 +330,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_volume' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Volume" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "volume" + @property def icon(self) -> str: """Set icon.""" @@ -357,7 +356,7 @@ def entity_category(self) -> EntityCategory: @property def native_value(self) -> int: - """Returns current volume setting.""" + """Returns current volume setting.""" return self.feeder_data.data['settings']['volume'] @@ -434,18 +433,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_manual_feed' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Manual feed" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "manual_feed" + @property def icon(self) -> str: """Set icon.""" @@ -454,7 +453,7 @@ def icon(self) -> str: @property def native_value(self) -> int: - """Returns lowest amount allowed.""" + """Returns lowest amount allowed.""" return 4 @@ -544,18 +543,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_cleaning_delay' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Cleaning delay" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "cleaning_delay" + @property def icon(self) -> str: """Set icon.""" @@ -570,13 +569,13 @@ def entity_category(self) -> EntityCategory: @property def native_value(self) -> int: - """Returns currently set delay in minutes.""" + """Returns currently set delay in minutes.""" return (self.lb_data.device_detail['settings']['stillTime'] / 60) @property - def native_unit_of_measurement(self) -> UnitOfMass: - """Return grams.""" + def native_unit_of_measurement(self) -> UnitOfTime: + """Return minutes.""" return UnitOfTime.MINUTES @@ -624,7 +623,7 @@ async def async_set_native_value(self, value: int) -> None: """Update the current value.""" seconds = int(value * 60) - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DELAYCLEANTIME, seconds) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DELAY_CLEAN_TIME, seconds) self.lb_data.device_detail['settings']['stillTime'] = seconds self.async_write_ha_state() await self.coordinator.async_request_refresh() diff --git a/custom_components/petkit/select.py b/custom_components/petkit/select.py index 4a701ee..786e6f2 100644 --- a/custom_components/petkit/select.py +++ b/custom_components/petkit/select.py @@ -63,18 +63,18 @@ async def async_setup_entry( for feeder_id, feeder_data in coordinator.data.feeders.items(): # D4 and Mini Feeders if feeder_data.type in ['d4', 'feedermini']: - selects.extend(( - ManualFeed(coordinator, feeder_id), - )) + selects.append( + ManualFeed(coordinator, feeder_id) + ) # D3 Feeder if feeder_data.type == 'd3': - selects.extend(( - Sound(coordinator, feeder_id), - )) + selects.append( + Sound(coordinator, feeder_id) + ) # Litter boxes for lb_id, lb_data in coordinator.data.litter_boxes.items(): - # Pura X + # Pura X & Pura MAX selects.extend(( LBCleaningInterval(coordinator, lb_id), LBLitterType(coordinator, lb_id), @@ -113,18 +113,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_light_brightness' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Light brightness" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "light_brightness" + @property def icon(self) -> str: """Set icon.""" @@ -215,18 +215,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_mode' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Mode" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "mode" + @property def icon(self) -> str: """Set icon.""" @@ -315,18 +315,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_manual_feed' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Manual feed" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "manual_feed" + @property def icon(self) -> str: """Set icon.""" @@ -397,18 +397,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_sound' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Sound" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "sound" + @property def icon(self) -> str: """Set icon.""" @@ -453,7 +453,7 @@ async def async_select_option(self, option: str) -> None: NAME_TO_SOUND_ID = {v: k for (k, v) in available_sounds.items()} ha_to_petkit = NAME_TO_SOUND_ID.get(option) - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SELECTEDSOUND, ha_to_petkit) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SELECTED_SOUND, ha_to_petkit) self.feeder_data.data['settings']['selectedSound'] = ha_to_petkit self.async_write_ha_state() await self.coordinator.async_request_refresh() @@ -490,18 +490,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_cleaning_interval' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Cleaning interval" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "cleaning_interval" + @property def icon(self) -> str: """Set icon.""" @@ -552,7 +552,7 @@ async def async_select_option(self, option: str) -> None: ha_to_petkit = CLEANING_INTERVAL_TO_PETKIT.get(option) - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.CLEANINTERVAL, ha_to_petkit) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.CLEAN_INTERVAL, ha_to_petkit) self.lb_data.device_detail['settings']['autoIntervalMin'] = ha_to_petkit self.async_write_ha_state() await self.coordinator.async_request_refresh() @@ -589,18 +589,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_litter_type' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Litter type" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "litter_type" + @property def icon(self) -> str: """Set icon.""" @@ -640,7 +640,7 @@ async def async_select_option(self, option: str) -> None: ha_to_petkit = LITTER_TYPE_TO_PETKIT.get(option) - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.SANDTYPE, ha_to_petkit) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.SAND_TYPE, ha_to_petkit) self.lb_data.device_detail['settings']['sandType'] = ha_to_petkit self.async_write_ha_state() await self.coordinator.async_request_refresh() diff --git a/custom_components/petkit/sensor.py b/custom_components/petkit/sensor.py index f95b80a..8f1b485 100644 --- a/custom_components/petkit/sensor.py +++ b/custom_components/petkit/sensor.py @@ -2,6 +2,7 @@ from __future__ import annotations from datetime import datetime +from math import floor as floor from typing import Any from petkitaio.model import Feeder, LitterBox, Pet, W5Fountain @@ -27,14 +28,19 @@ from .const import ( DOMAIN, - EVENT_DESCRIPTION, - EVENT_TYPE_NAMED, FEEDERS, LITTER_BOXES, - VALID_EVENT_TYPES, WATER_FOUNTAINS ) from .coordinator import PetKitDataUpdateCoordinator +from .litter_events import ( + EVENT_DESCRIPTION, + EVENT_TYPE_NAMED, + MAX_EVENT_DESCRIPTION, + MAX_EVENT_TYPES, + MAX_EVENT_TYPE_NAMED, + VALID_EVENT_TYPES +) async def async_setup_entry( @@ -76,9 +82,9 @@ async def async_setup_entry( # D4 Feeder if feeder_data.type == 'd4': - sensors.extend(( - ManualDispensed(coordinator, feeder_id), - )) + sensors.append( + ManualDispensed(coordinator, feeder_id) + ) #D3 Feeder if feeder_data.type == 'd3': @@ -90,19 +96,36 @@ async def async_setup_entry( # Litter boxes for lb_id, lb_data in coordinator.data.litter_boxes.items(): + #Pura Air device for MAX litter box + if (lb_data.type == 't4') and ('k3Device' in lb_data.device_detail): + sensors.extend(( + PuraAirBattery(coordinator, lb_id), + PuraAirLiquid(coordinator, lb_id) + )) + # Pura X & MAX + if lb_data.type in ['t3', 't4']: + sensors.extend(( + LBDeodorizerLevel(coordinator, lb_id), + LBLitterLevel(coordinator, lb_id), + LBLitterWeight(coordinator, lb_id), + LBRSSI(coordinator, lb_id), + LBError(coordinator, lb_id), + LBTimesUsed(coordinator, lb_id), + LBAverageUse(coordinator, lb_id), + LBTotalUse(coordinator, lb_id), + LBLastUsedBy(coordinator, lb_id) + )) # Pura X - sensors.extend(( - LBDeodorizerLevel(coordinator, lb_id), - LBLitterLevel(coordinator, lb_id), - LBLitterWeight(coordinator, lb_id), - LBRSSI(coordinator, lb_id), - LBError(coordinator, lb_id), - LBTimesUsed(coordinator, lb_id), - LBAverageUse(coordinator, lb_id), - LBTotalUse(coordinator, lb_id), - LBLastUsedBy(coordinator, lb_id), - LBLastEvent(coordinator, lb_id), - )) + if lb_data.type == 't3': + sensors.append( + LBLastEvent(coordinator, lb_id) + ) + # Pura MAX + if lb_data.type == 't4': + sensors.extend(( + MAXLastEvent(coordinator, lb_id), + MAXWorkState(coordinator, lb_id) + )) # Pets for pet_id, pet_data in coordinator.data.pets.items(): @@ -147,18 +170,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_energy_usage' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Energy usage" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "energy_usage" + @property def native_value(self) -> float: """Return total energy usage in kWh.""" @@ -222,18 +245,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_last_update' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Last data update" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "last_data_update" + @property def native_value(self) -> datetime: """Return date/time of last water fountain data update. @@ -302,18 +325,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_filter_percent' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Filter" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "filter" + @property def native_value(self) -> int: """Return current filter percent left.""" @@ -372,18 +395,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_purified_water' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Purified water today" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "purified_water_today" + @property def native_value(self) -> int: """Return number of times water was purified today.""" @@ -442,29 +465,29 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_status' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Status" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "feeder_status" + @property def native_value(self) -> str | None: """Return status of the feeder.""" pim = self.feeder_data.data['state']['pim'] if pim == 0: - return 'Offline' + return 'offline' elif pim == 1: - return 'Normal' + return 'normal' elif pim == 2: - return 'On Batteries' + return 'on_batteries' else: return None @@ -519,18 +542,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_desiccant_days' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Desiccant days remaining" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "desiccant_days_remaining" + @property def native_value(self) -> int: """Return days remaining.""" @@ -586,27 +609,27 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_battery_status' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Battery status" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "battery_status" + @property def native_value(self) -> str: - """Return status of the feeder.""" + """Return status of the feeder battery.""" battery_level = self.feeder_data.data['state']['batteryStatus'] if battery_level == 1: - return "Normal" + return "normal" else: - return "Low" + return "low" @property def entity_category(self) -> EntityCategory: @@ -667,18 +690,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_total_dispensed' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Dispensed" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "dispensed" + @property def native_value(self) -> int: """Return total dispensed.""" @@ -740,18 +763,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_total_planned' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Planned" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "planned" + @property def native_value(self) -> int: """Return total planned.""" @@ -813,18 +836,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_planned_dispensed' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Planned dispensed" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "planned_dispensed" + @property def native_value(self) -> int: """Return total planned dispensed.""" @@ -886,18 +909,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_manual_dispensed' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Manually dispensed" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "manually_dispensed" + @property def native_value(self) -> int: """Return total manually dispensed.""" @@ -959,18 +982,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_times_dispensed' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Times dispensed" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "times_dispensed" + @property def native_value(self) -> int: """Return total times dispensed.""" @@ -1029,18 +1052,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_feeder_rssi' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "RSSI" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "rssi" + @property def native_value(self) -> int: """Return RSSI measurement.""" @@ -1109,18 +1132,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_amount_eaten' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Amount eaten" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "amount_eaten" + @property def native_value(self) -> int: """Return total amount eaten.""" @@ -1184,16 +1207,17 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_times_eaten' @property - def name(self) -> str: - """Return name of the entity.""" - - return "Times eaten" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "times_eaten" + @property def native_value(self) -> int: """Return total times eaten.""" @@ -1250,18 +1274,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_food_in_bowl' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Food in bowl" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "food_in_bowl" + @property def native_value(self) -> int: """Return current amount of food in bowl.""" @@ -1324,18 +1348,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_feeder_error' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Error" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "error" + @property def native_value(self) -> str: """Return current error if there is one.""" @@ -1343,7 +1367,7 @@ def native_value(self) -> str: if 'errorMsg' in self.feeder_data.data['state']: return self.feeder_data.data['state']['errorMsg'] else: - return 'No Error' + return 'no_error' @property def entity_category(self) -> EntityCategory: @@ -1359,7 +1383,7 @@ def icon(self) -> str: class LBDeodorizerLevel(CoordinatorEntity, SensorEntity): - """Representation of litter box deodorizer percentage left.""" + """Representation of litter box deodorizer left.""" def __init__(self, coordinator, lb_id): super().__init__(coordinator) @@ -1389,29 +1413,45 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_deodorizer_level' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Deodorizer level" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + #Pura MAX uses N50 deodorizer and not liquid + if self.lb_data.type == 't4': + return "n50_odor_eliminator" + else: + return "deodorizer_level" + @property def icon(self) -> str: """Set icon.""" - return 'mdi:spray-bottle' + if self.lb_data.type == 't4': + return 'mdi:air-filter' + else: + return 'mdi:spray-bottle' @property def native_value(self) -> int: - """Return current percentage.""" + """Return current percentage or days left.""" - return self.lb_data.device_detail['state']['liquid'] + #Pura MAX + if self.lb_data.type == 't4': + deodorant_days = self.lb_data.device_detail['state']['deodorantLeftDays'] + if deodorant_days < 1: + return 0 + else: + return deodorant_days + #Pura X + else: + return self.lb_data.device_detail['state']['liquid'] @property def entity_category(self) -> EntityCategory: @@ -1426,10 +1466,15 @@ def state_class(self) -> SensorStateClass: return SensorStateClass.MEASUREMENT @property - def native_unit_of_measurement(self) -> str: - """Return percent as the native unit.""" + def native_unit_of_measurement(self) -> str | UnitOfTime: + """Return percent or days as the native unit.""" - return PERCENTAGE + #Pura MAX + if self.lb_data.type == 't4': + return UnitOfTime.DAYS + #Pura X + else: + return PERCENTAGE class LBLitterLevel(CoordinatorEntity, SensorEntity): """Representation of litter box litter percentage left.""" @@ -1462,18 +1507,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_litter_level' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Litter level" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "litter_level" + @property def icon(self) -> str: """Set icon.""" @@ -1536,18 +1581,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_litter_weight' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Litter weight" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "litter_weight" + @property def icon(self) -> str: """Set icon.""" @@ -1616,18 +1661,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_rssi' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "RSSI" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "rssi" + @property def icon(self) -> str: """Set icon.""" @@ -1696,18 +1741,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_error' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Error" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "error" + @property def native_value(self) -> str: """Return current error if there is one.""" @@ -1715,7 +1760,7 @@ def native_value(self) -> str: if 'errorMsg' in self.lb_data.device_detail['state']: return self.lb_data.device_detail['state']['errorMsg'] else: - return 'No Error' + return 'no_error' @property def entity_category(self) -> EntityCategory: @@ -1761,18 +1806,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_times_used' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Times used" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "times_used" + @property def native_value(self) -> int: """Return current usage count.""" @@ -1823,18 +1868,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_average_use' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Average use" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "average_use" + @property def native_value(self) -> int: """Return current usage time average in seconds.""" @@ -1891,18 +1936,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_total_use' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Total use" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "total_use" + @property def native_value(self) -> int: """Return current usage time average in seconds.""" @@ -1959,18 +2004,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_last_used_by' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Last used by" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "last_used_by" + @property def native_value(self) -> str: """Return last pet to use the litter box.""" @@ -1978,11 +2023,11 @@ def native_value(self) -> str: if self.lb_data.statistics['statisticInfo']: last_record = self.lb_data.statistics['statisticInfo'][-1] if last_record['petId'] == '0': - return 'Unknown pet' + return 'unknown_pet' else: return last_record['petName'] else: - return 'No record yet' + return 'no_record_yet' @property def icon(self) -> str: @@ -1992,7 +2037,7 @@ def icon(self) -> str: class LBLastEvent(CoordinatorEntity, SensorEntity): - """Representation of last litter box event.""" + """Representation of Pura X last litter box event.""" def __init__(self, coordinator, lb_id): super().__init__(coordinator) @@ -2023,18 +2068,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_last_event' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Last event" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "last_event" + @property def native_value(self) -> str: """Return last litter box event from device record.""" @@ -2044,11 +2089,11 @@ def native_value(self) -> str: if last_record['subContent']: self.sub_events = self.sub_events_to_description(last_record['subContent']) else: - self.sub_events = None + self.sub_events = 'no_sub_events' event = self.result_to_description(last_record['eventType'], last_record) return event else: - return 'No events yet' + return 'no_events_yet' @property def extra_state_attributes(self): @@ -2075,7 +2120,7 @@ def result_to_description(self, event_type: int, record: dict[str, Any]) -> str: # Make sure event_type is valid if event_type not in VALID_EVENT_TYPES: - return 'Event type unknown' + return 'event_type_unknown' # Pet out events don't have result or reason if event_type != 10: @@ -2083,53 +2128,53 @@ def result_to_description(self, event_type: int, record: dict[str, Any]) -> str: if 'startReason' in record['content']: reason = record['content']['startReason'] - if event_type == 5: - if result == 2: - if 'error' in record['content']: - error = record['content']['error'] - else: - return EVENT_TYPE_NAMED[event_type] - - try: - description = EVENT_DESCRIPTION[event_type][result][reason][error] - except KeyError: - return EVENT_TYPE_NAMED[event_type] - return description + if event_type == 5: + if result == 2: + if 'error' in record['content']: + error = record['content']['error'] + else: + return EVENT_TYPE_NAMED[event_type] - else: - try: - description = EVENT_DESCRIPTION[event_type][result][reason] - except KeyError: - return EVENT_TYPE_NAMED[event_type] - return description + try: + description = EVENT_DESCRIPTION[event_type][result][reason][error] + except KeyError: + return EVENT_TYPE_NAMED[event_type] + return description - if event_type in [6, 7]: - if result == 2: - if 'error' in record['content']: - error = record['content']['error'] else: - return EVENT_TYPE_NAMED[event_type] + try: + description = EVENT_DESCRIPTION[event_type][result][reason] + except KeyError: + return EVENT_TYPE_NAMED[event_type] + return description + + if event_type in [6, 7]: + if result == 2: + if 'error' in record['content']: + error = record['content']['error'] + else: + return EVENT_TYPE_NAMED[event_type] + + try: + description = EVENT_DESCRIPTION[event_type][result][error] + except KeyError: + return EVENT_TYPE_NAMED[event_type] + return description - try: - description = EVENT_DESCRIPTION[event_type][result][error] - except KeyError: - return EVENT_TYPE_NAMED[event_type] - return description + else: + try: + description = EVENT_DESCRIPTION[event_type][result] + except KeyError: + return EVENT_TYPE_NAMED[event_type] + return description - else: + if event_type == 8: try: - description = EVENT_DESCRIPTION[event_type][result] + description = EVENT_DESCRIPTION[event_type][result][reason] except KeyError: return EVENT_TYPE_NAMED[event_type] return description - if event_type == 8: - try: - description = EVENT_DESCRIPTION[event_type][result][reason] - except KeyError: - return EVENT_TYPE_NAMED[event_type] - return description - if event_type == 10: if (record['petId'] == '-2') or (record['petId'] == '-1'): name = 'Unknown' @@ -2162,7 +2207,7 @@ def pet_data(self) -> Pet: @property def litter_boxes(self) -> dict[LitterBox, Any]: """Handle coordinator Litter Boxes data.""" - + return self.coordinator.data.litter_boxes @property @@ -2182,12 +2227,6 @@ def unique_id(self) -> str: return self.pet_data.id + '_recent_weight' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Latest weight" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" @@ -2195,7 +2234,13 @@ def has_entity_name(self) -> bool: return True @property - def entity_picture(self) -> str: + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "latest_weight" + + @property + def entity_picture(self) -> str | None: """Grab associated pet picture.""" if 'avatar' in self.pet_data.data: @@ -2204,7 +2249,7 @@ def entity_picture(self) -> str: return None @property - def icon(self) -> str: + def icon(self) -> str | None: """Set icon if the pet doesn't have an avatar.""" if 'avatar' in self.pet_data.data: @@ -2245,7 +2290,7 @@ def state_class(self) -> SensorStateClass: def grab_recent_weight(self) -> float: """Grab the most recent weight.""" - + weight_dict: dict[int, int] = {} for lb_id, lb_data in self.litter_boxes.items(): @@ -2279,7 +2324,7 @@ def pet_data(self) -> Pet: @property def litter_boxes(self) -> dict[LitterBox, Any]: """Handle coordinator Litter Boxes data.""" - + return self.coordinator.data.litter_boxes @property @@ -2299,12 +2344,6 @@ def unique_id(self) -> str: return self.pet_data.id + '_last_use_duration' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Last use duration" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" @@ -2312,7 +2351,13 @@ def has_entity_name(self) -> bool: return True @property - def entity_picture(self) -> str: + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "last_use_duration" + + @property + def entity_picture(self) -> str | None: """Grab associated pet picture.""" if 'avatar' in self.pet_data.data: @@ -2321,7 +2366,7 @@ def entity_picture(self) -> str: return None @property - def icon(self) -> str: + def icon(self) -> str | None: """Set icon if the pet doesn't have an avatar.""" if 'avatar' in self.pet_data.data: @@ -2355,7 +2400,7 @@ def state_class(self) -> SensorStateClass: def grab_recent_duration(self) -> float: """Grab the most recent duration.""" - + duration_dict: dict[int, int] = {} for lb_id, lb_data in self.litter_boxes.items(): @@ -2372,3 +2417,543 @@ def grab_recent_duration(self) -> float: duration_dict[time] = duration sorted_dict = dict(sorted(duration_dict.items())) return sorted_dict + + +class PuraAirBattery(CoordinatorEntity, SensorEntity): + """Representation of Pura Air battery level.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_pura_air_battery' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "pura_air_battery" + + @property + def native_value(self) -> int: + """Return current battery percentage.""" + + return self.lb_data.device_detail['k3Device']['battery'] + + @property + def native_unit_of_measurement(self) -> str: + """Return % as the native unit.""" + + return PERCENTAGE + + @property + def device_class(self) -> SensorDeviceClass: + """ Return entity device class """ + + return SensorDeviceClass.BATTERY + + @property + def entity_category(self) -> EntityCategory: + """Set category to diagnostic.""" + + return EntityCategory.DIAGNOSTIC + + @property + def state_class(self) -> SensorStateClass: + """Return the type of state class.""" + + return SensorStateClass.MEASUREMENT + + @property + def available(self) -> bool: + """Determine if device is available. + + Return true if there is a pura air + device associated. + """ + + if 'k3Device' in self.lb_data.device_detail: + return True + else: + return False + + +class PuraAirLiquid(CoordinatorEntity, SensorEntity): + """Representation of Pura Air liquid level.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_pura_air_liquid' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "pura_air_liquid" + + @property + def native_value(self) -> int: + """Return current liquid left.""" + + return self.lb_data.device_detail['k3Device']['liquid'] + + @property + def native_unit_of_measurement(self) -> str: + """Return % as the native unit.""" + + return PERCENTAGE + + @property + def entity_category(self) -> EntityCategory: + """Set category to diagnostic.""" + + return EntityCategory.DIAGNOSTIC + + @property + def state_class(self) -> SensorStateClass: + """Return the type of state class.""" + + return SensorStateClass.MEASUREMENT + + @property + def icon(self) -> str: + """Return icon for entity.""" + + return 'mdi:cup' + + @property + def available(self) -> bool: + """Determine if device is available. + + Return true if there is a pura air + device associated. + """ + + if 'k3Device' in self.lb_data.device_detail: + return True + else: + return False + + +class MAXLastEvent(CoordinatorEntity, SensorEntity): + """Representation of last Pura MAX litter box event.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + self.sub_events = None + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_max_last_event' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "max_last_event" + + @property + def native_value(self) -> str: + """Return last litter box event from device record.""" + + if self.lb_data.device_record: + last_record = self.lb_data.device_record[-1] + if last_record['subContent']: + self.sub_events = self.sub_events_to_description(last_record['subContent']) + else: + self.sub_events = 'no_sub_events' + event = self.result_to_description(last_record['eventType'], last_record) + return event + else: + return 'no_events_yet' + + @property + def extra_state_attributes(self): + """Return sub events associated with the main event.""" + + return { + 'sub_events': self.sub_events + } + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:calendar' + + @property + def entity_category(self) -> EntityCategory: + """Set category to diagnostic.""" + + return EntityCategory.DIAGNOSTIC + + def result_to_description(self, event_type: int, record: dict[str, Any]) -> str: + """Return a description of the last event""" + + # Make sure event_type is valid + if event_type not in MAX_EVENT_TYPES: + return 'event_type_unknown' + + # Pet out events don't have result or reason + if event_type != 10: + result = record['content']['result'] + if 'startReason' in record['content']: + reason = record['content']['startReason'] + + if event_type == 5: + if result == 2: + if 'error' in record['content']: + error = record['content']['error'] + else: + return MAX_EVENT_TYPE_NAMED[event_type] + + try: + description = MAX_EVENT_DESCRIPTION[event_type][result][reason][error] + except KeyError: + if reason == 0: + return 'auto_cleaning_failed_other' + elif reason == 1: + return 'scheduled_cleaning_failed_other' + else: + return 'manual_cleaning_failed_other' + return description + + else: + try: + description = MAX_EVENT_DESCRIPTION[event_type][result][reason] + except KeyError: + return MAX_EVENT_TYPE_NAMED[event_type] + return description + + if event_type in [6, 7]: + if result == 2: + if 'error' in record['content']: + error = record['content']['error'] + else: + return MAX_EVENT_TYPE_NAMED[event_type] + + try: + description = MAX_EVENT_DESCRIPTION[event_type][result][error] + except KeyError: + if event_type == 6: + return 'litter_empty_failed_other' + else: + return 'reset_failed_other' + return description + + else: + try: + description = MAX_EVENT_DESCRIPTION[event_type][result] + except KeyError: + return MAX_EVENT_TYPE_NAMED[event_type] + return description + + if event_type == 8: + try: + if result == 9: + return 'cat_stopped_odor' + else: + description = MAX_EVENT_DESCRIPTION[event_type][result][reason] + except KeyError: + return MAX_EVENT_TYPE_NAMED[event_type] + return description + + if event_type == 17: + try: + description = MAX_EVENT_DESCRIPTION[event_type][result] + except KeyError: + return MAX_EVENT_TYPE_NAMED[event_type] + return description + + if event_type == 10: + if (record['petId'] == '-2') or (record['petId'] == '-1'): + name = 'Unknown' + else: + name = record['petName'] + return f'{name} used the litter box' + + def sub_events_to_description(self, sub_events: list[dict[str, Any]]) -> list[str]: + """Create a list containing all the sub-events associated with an event to be used as attribute""" + + event_list: list[str] = [] + for event in sub_events: + description = self.result_to_description(event['eventType'], event) + event_list.append(description) + return event_list + + +class MAXWorkState(CoordinatorEntity, SensorEntity): + """Representation of current Pura MAX litter box state.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + self.sub_events = None + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_max_work_state' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "max_work_state" + + @property + def native_value(self) -> str: + """Return current litter box work state from device_detail.""" + + if 'workState' in self.lb_data.device_detail['state']: + work_state = self.lb_data.device_detail['state']['workState'] + + if work_state['workMode'] == 0: + work_process = work_state['workProcess'] + if work_process / 10 == 1: + return 'cleaning_litter_box' + if int(floor((work_process / 10))) == 2: + if work_process % 10 == 2: + if 'safeWarn' in work_state: + if work_state['safeWarn'] != 0: + if work_state['safeWarn'] == 1: + return 'cleaning_paused_pet_entered' + else: + return 'cleaning_paused_system_error' + if work_state['safeWarn'] == 0: + ### petInTime could be referring to key in state and not workState + if work_state['petInTime'] == 0: + return 'cleaning_paused_pet_approach' + else: + return 'cleaning_paused_pet_using' + else: + return 'cleaning_litter_box_paused' + if work_process / 10 == 3: + return 'resetting_device' + if int(floor((work_process / 10))) == 4: + if work_process % 10 == 2: + if 'safeWarn' in work_state: + if work_state['safeWarn'] != 0: + if work_state['safeWarn'] == 1: + return 'paused_pet_entered' + else: + return 'paused_system_error' + if work_state['safeWarn'] == 0: + ### petInTime could be referring to key in state and not workState + if work_state['petInTime'] == 0: + return 'paused_pet_approach' + else: + return 'paused_pet_using' + else: + return 'litter_box_paused' + + if work_state['workMode'] == 1: + work_process = work_state['workProcess'] + if work_process / 10 == 1: + return 'dumping_litter' + if int(floor((work_process / 10))) == 2: + if work_process % 10 == 2: + if 'safeWarn' in work_state: + if work_state['safeWarn'] != 0: + if work_state['safeWarn'] == 1: + return 'dumping_paused_pet_entered' + else: + return 'dumping_paused_system_error' + if work_state['safeWarn'] == 0: + ### petInTime could be referring to key in state and not workState + if work_state['petInTime'] == 0: + return 'dumping_paused_pet_approach' + else: + return 'dumping_paused_pet_using' + else: + return 'dumping_litter_paused' + if work_process / 10 == 3: + return 'resetting_device' + if int(floor((work_process / 10))) == 4: + if work_process % 10 == 2: + if 'safeWarn' in work_state: + if work_state['safeWarn'] != 0: + if work_state['safeWarn'] == 1: + return 'paused_pet_entered' + else: + return 'paused_system_error' + if work_state['safeWarn'] == 0: + ### petInTime could be referring to key in state and not workState + if work_state['petInTime'] == 0: + return 'paused_pet_approach' + else: + return 'paused_pet_using' + else: + return 'litter_box_paused' + if work_state['workMode'] == 3: + return 'resetting' + if work_state['workMode'] == 4: + return 'leveling' + if work_state['workMode'] == 5: + return 'calibrating' + if work_state['workMode'] == 9: + work_process = work_state['workProcess'] + if work_process / 10 == 1: + return 'maintenance_mode' + if int(floor((work_process / 10))) == 2: + if work_process % 10 == 2: + if 'safeWarn' in work_state: + if work_state['safeWarn'] != 0: + if work_state['safeWarn'] == 1: + return 'maintenance_paused_pet_entered' + elif work_state['safeWarn'] == 3: + return 'maintenance_paused_cover' + else: + return 'maintenance_paused_system_error' + if work_state['safeWarn'] == 0: + ### petInTime could be referring to key in state and not workState + if work_state['petInTime'] == 0: + return 'maintenance_paused_pet_approach' + else: + return 'maintenance_paused_pet_using' + else: + return 'maintenance_paused' + if work_process / 10 == 3: + return 'exit_maintenance' + if int(floor((work_process / 10))) == 4: + if work_process % 10 == 2: + if 'safeWarn' in work_state: + if work_state['safeWarn'] != 0: + if work_state['safeWarn'] == 1: + return 'maintenance_exit_paused_pet_entered' + elif work_state['safeWarn'] == 3: + return 'maintenance_exit_paused_cover' + else: + return 'maintenance_exit_paused_system_error' + if work_state['safeWarn'] == 0: + ### petInTime could be referring to key in state and not workState + if work_state['petInTime'] == 0: + return 'maintenance_exit_paused_pet_approach' + else: + return 'maintenance_exit_paused_pet_using' + else: + return 'maintenance_exit_paused' + else: + return 'idle' + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:account-hard-hat' + + @property + def entity_category(self) -> EntityCategory: + """Set category to diagnostic.""" + + return EntityCategory.DIAGNOSTIC diff --git a/custom_components/petkit/strings.json b/custom_components/petkit/strings.json index a21b822..cb89fd6 100644 --- a/custom_components/petkit/strings.json +++ b/custom_components/petkit/strings.json @@ -19,11 +19,625 @@ "error": { "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication. Are your credentials correct?", - "no_devices": "No devices found on account" + "no_devices": "No devices found on account", + "server_busy": "PetKit servers are busy. Please try again later." }, "abort": { "already_configured": "PetKit account is already configured", "reauth_successful": "Reauthentication was successful" } + }, + "entity": { + "binary_sensor": { + "water_level": { + "name": "Water level" + }, + "food_level": { + "name": "Food level" + }, + "battery_installed": { + "name": "Battery installed" + }, + "battery": { + "name": "Battery" + }, + "wastebin": { + "name": "Wastebin" + }, + "litter": { + "name": "Litter" + }, + "deodorizer": { + "name": "Deodorizer" + }, + "manually_paused": { + "name": "Manually paused" + }, + "pura_air_liquid": { + "name": "Pura Air liquid" + } + }, + "button": { + "reset_filter": { + "name": "Reset filter" + }, + "reset_desiccant": { + "name": "Reset desiccant" + }, + "cancel_manual_feed": { + "name": "Cancel manual feed" + }, + "call_pet": { + "name": "Call pet" + }, + "start_cleaning": { + "name": "Start/Resume cleaning" + }, + "pause_cleaning": { + "name": "Pause cleaning" + }, + "odor_removal": { + "name": "Odor removal" + }, + "reset_deodorizer": { + "name": "Reset deodorizer" + }, + "reset_pura_air_liquid": { + "name": "Reset Pura Air liquid" + }, + "n50_reset": { + "name": "Reset N50 odor eliminator" + }, + "light_on": { + "name": "Turn light ON" + }, + "start_maintenance": { + "name": "Start maintenance mode" + }, + "exit_maintenance": { + "name": "Exit maintenance mode" + }, + "pause_exit_maintenance": { + "name": "Pause exiting maintenance mode" + }, + "resume_exit_maintenance": { + "name": "Resume exiting maintenance mode" + }, + "dump_litter": { + "name": "Dump litter" + }, + "pause_dump_litter": { + "name": "Pause litter dumping" + }, + "resume_dump_litter": { + "name": "Resume litter dumping" + } + }, + "number": { + "set_weight": { + "name": "Set weight" + }, + "surplus": { + "name": "Surplus" + }, + "volume": { + "name": "Volume" + }, + "manual_feed": { + "name": "Manual feed" + }, + "cleaning_delay": { + "name": "Cleaning delay" + } + }, + "select": { + "light_brightness": { + "name": "Light brightness", + "state": { + "low": "Low", + "medium": "Medium", + "high": "High" + } + }, + "mode": { + "name": "Mode", + "state": { + "normal": "Normal", + "smart": "Smart" + } + }, + "manual_feed": { + "name": "Manual feed" + }, + "sound": { + "name": "Sound" + }, + "cleaning_interval": { + "name": "Cleaning interval" + }, + "litter_type": { + "name": "Litter type", + "state": { + "bentonite": "Bentonite", + "tofu": "Tofu", + "mixed": "Mixed" + } + } + }, + "sensor": { + "energy_usage": { + "name": "Energy usage" + }, + "last_data_update": { + "name": "Last data update" + }, + "filter": { + "name": "Filter" + }, + "purified_water_today": { + "name": "Purified water today" + }, + "feeder_status": { + "name": "Status", + "state": { + "offline": "Offline", + "normal": "Normal", + "on_batteries": "On Batteries" + } + }, + "desiccant_days_remaining": { + "name": "Desiccant days remaining" + }, + "battery_status": { + "name": "Battery status", + "state": { + "normal": "Normal", + "low": "Low" + } + }, + "dispensed": { + "name": "Dispensed" + }, + "planned": { + "name": "Planned" + }, + "planned_dispensed": { + "name": "Planned dispensed" + }, + "manually_dispensed": { + "name": "Manually dispensed" + }, + "times_dispensed": { + "name": "Times dispensed" + }, + "rssi": { + "name": "RSSI" + }, + "amount_eaten": { + "name": "Amount eaten" + }, + "times_eaten": { + "name": "Times eaten" + }, + "food_in_bowl": { + "name": "Food in bowl" + }, + "error": { + "name": "Error", + "state": { + "no_error": "No Error" + } + }, + "deodorizer_level": { + "name": "Deodorizer level" + }, + "litter_level": { + "name": "Litter level" + }, + "litter_weight": { + "name": "Litter weight" + }, + "times_used": { + "name": "Times used" + }, + "average_use": { + "name": "Average use" + }, + "total_use": { + "name": "Total use" + }, + "last_used_by": { + "name": "Last used by", + "state": { + "no_record_yet": "No record yet", + "unknown_pet": "Unknown pet" + } + }, + "last_event": { + "name": "Last event", + "state": { + "no_events_yet": "No events yet", + "event_type_unknown": "Event type unknown", + "cleaning_completed": "Cleaning completed", + "dumping_over": "Dumping Over", + "reset_over": "Reset over", + "spray_over": "Spray over", + "pet_out": "Pet out", + "auto_cleaning_completed": "Auto cleaning completed", + "periodic_cleaning_completed": "Periodic cleaning completed", + "manual_cleaning_completed": "Manual cleaning completed", + "auto_cleaning_terminated": "Automatic cleaning terminated", + "periodic_cleaning_terminated": "Periodic cleaning terminated", + "manual_cleaning_terminated": "Manual cleaning terminated", + "auto_cleaning_failed_full": "Automatic cleaning failed, waste collection bin is full, please empty promptly", + "auto_cleaning_failed_hall_l": "Automatic cleaning failure, the cylinder is not properly locked in place, please check", + "auto_cleaning_failed_hall_t": "Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "scheduled_cleaning_failed_full": "Scheduled cleaning failed, waste collection bin is full, please empty promptly", + "scheduled_cleaning_failed_hall_l": "Scheduled cleaning failure, the cylinder is not properly locked in place, please check", + "scheduled_cleaning_failed_hall_t": "Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "manual_cleaning_failed_full": "Manual cleaning failed, waste collection bin is full, please empty promptly", + "manual_cleaning_failed_hall_l": "Manual cleaning failure, the cylinder is not properly locked in place, please check", + "manual_cleaning_failed_hall_t": "Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "auto_cleaning_canceled": "Automatic cleaning canceled, device in operation", + "periodic_cleaning_canceled": "Periodic cleaning canceled, device in operation", + "manual_cleaning_canceled": "Manual cleaning canceled, device in operation", + "auto_cleaning_canceled_kitten": "Kitten mode is enabled, auto cleaning is canceled", + "periodic_cleaning_canceled_kitten": "Kitten mode is enabled, periodically cleaning is canceled", + "litter_empty_completed": "Cat litter empty completed", + "litter_empty_terminated": "Cat litter empty terminated", + "litter_empty_failed_full": "Cat litter empty failed, waste collection bin is full, please empty promptly", + "litter_empty_failed_hall_l": "Cat litter empty failure, the cylinder is not properly locked in place, please check", + "litter_empty_failed_hall_t": "Cat litter empty failure, the litter box's cupper cover is not placed properly, please check", + "reset_completed": "Device reset completed", + "reset_terminated": "Device reset terminated", + "reset_failed_full": "Device reset failed, waste collection bin is full, please empty promptly", + "reset_failed_hall_l": "Device reset failure, the cylinder is not properly locked in place, please check", + "reset_failed_hall_t": "Device reset failure, the litter box's cupper cover is not placed properly, please check", + "deodorant_finished": "Deodorant finished", + "periodic_odor_completed": "Periodic odor removal completed", + "manual_odor_completed": "Manual odor removal completed", + "deodorant_finished_liquid_lack": "Deodorant finished, not enough purifying liquid, please refill in time", + "periodic_odor_completed_liquid_lack": "Periodic odor removal completed, not enough purifying liquid, please refill in time", + "manual_odor_completed_liquid_lack": "Manual odor removal completed, not enough purifying liquid, please refill in time", + "auto_odor_failed": "Automatic odor removal failed, odor eliminator error", + "periodic_odor_failed": "Periodic odor removal failure, odor eliminator malfunction", + "manual_odor_failed": "Manual odor removal failure, odor eliminator malfunction" + }, + "state_attributes": { + "sub_events": { + "state": { + "no_events_yet": "No events yet", + "event_type_unknown": "Event type unknown", + "cleaning_completed": "Cleaning completed", + "dumping_over": "Dumping Over", + "reset_over": "Reset over", + "spray_over": "Spray over", + "pet_out": "Pet out", + "auto_cleaning_completed": "Auto cleaning completed", + "periodic_cleaning_completed": "Periodic cleaning completed", + "manual_cleaning_completed": "Manual cleaning completed", + "auto_cleaning_terminated": "Automatic cleaning terminated", + "periodic_cleaning_terminated": "Periodic cleaning terminated", + "manual_cleaning_terminated": "Manual cleaning terminated", + "auto_cleaning_failed_full": "Automatic cleaning failed, waste collection bin is full, please empty promptly", + "auto_cleaning_failed_hall_l": "Automatic cleaning failure, the cylinder is not properly locked in place, please check", + "auto_cleaning_failed_hall_t": "Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "scheduled_cleaning_failed_full": "Scheduled cleaning failed, waste collection bin is full, please empty promptly", + "scheduled_cleaning_failed_hall_l": "Scheduled cleaning failure, the cylinder is not properly locked in place, please check", + "scheduled_cleaning_failed_hall_t": "Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "manual_cleaning_failed_full": "Manual cleaning failed, waste collection bin is full, please empty promptly", + "manual_cleaning_failed_hall_l": "Manual cleaning failure, the cylinder is not properly locked in place, please check", + "manual_cleaning_failed_hall_t": "Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "auto_cleaning_canceled": "Automatic cleaning canceled, device in operation", + "periodic_cleaning_canceled": "Periodic cleaning canceled, device in operation", + "manual_cleaning_canceled": "Manual cleaning canceled, device in operation", + "auto_cleaning_canceled_kitten": "Kitten mode is enabled, auto cleaning is canceled", + "periodic_cleaning_canceled_kitten": "Kitten mode is enabled, periodically cleaning is canceled", + "litter_empty_completed": "Cat litter empty completed", + "litter_empty_terminated": "Cat litter empty terminated", + "litter_empty_failed_full": "Cat litter empty failed, waste collection bin is full, please empty promptly", + "litter_empty_failed_hall_l": "Cat litter empty failure, the cylinder is not properly locked in place, please check", + "litter_empty_failed_hall_t": "Cat litter empty failure, the litter box's cupper cover is not placed properly, please check", + "reset_completed": "Device reset completed", + "reset_terminated": "Device reset terminated", + "reset_failed_full": "Device reset failed, waste collection bin is full, please empty promptly", + "reset_failed_hall_l": "Device reset failure, the cylinder is not properly locked in place, please check", + "reset_failed_hall_t": "Device reset failure, the litter box's cupper cover is not placed properly, please check", + "deodorant_finished": "Deodorant finished", + "periodic_odor_completed": "Periodic odor removal completed", + "manual_odor_completed": "Manual odor removal completed", + "deodorant_finished_liquid_lack": "Deodorant finished, not enough purifying liquid, please refill in time", + "periodic_odor_completed_liquid_lack": "Periodic odor removal completed, not enough purifying liquid, please refill in time", + "manual_odor_completed_liquid_lack": "Manual odor removal completed, not enough purifying liquid, please refill in time", + "auto_odor_failed": "Automatic odor removal failed, odor eliminator error", + "periodic_odor_failed": "Periodic odor removal failure, odor eliminator malfunction", + "manual_odor_failed": "Manual odor removal failure, odor eliminator malfunction", + "no_sub_events": "No associated sub events" + } + } + } + }, + "latest_weight": { + "name": "Latest weight" + }, + "last_use_duration": { + "name": "Last use duration" + }, + "pura_air_battery": { + "name": "Pura Air battery" + }, + "pura_air_liquid": { + "name": "Pura Air liquid" + }, + "n50_odor_eliminator": { + "name": "N50 odor eliminator" + }, + "max_last_event": { + "name": "Last event", + "state": { + "no_events_yet": "No events yet", + "event_type_unknown": "Event type unknown", + "cleaning_completed": "Cleaning completed", + "dumping_over": "Dumping Over", + "reset_over": "Reset over", + "spray_over": "Spray over", + "light_over": "Light over", + "pet_out": "Pet out", + "auto_cleaning_completed": "Auto cleaning completed", + "periodic_cleaning_completed": "Periodic cleaning completed", + "manual_cleaning_completed": "Manual cleaning completed", + "auto_cleaning_terminated": "Automatic cleaning terminated", + "periodic_cleaning_terminated": "Periodic cleaning terminated", + "manual_cleaning_terminated": "Manual cleaning terminated", + "auto_cleaning_failed_full": "Automatic cleaning failed, waste collection bin is full, please empty promptly", + "auto_cleaning_failed_hall_t": "Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "auto_cleaning_failed_falldown": "Automatic cleaning failure, the litter box has been knocked down, please check.", + "auto_cleaning_failed_other": "Automatic cleaning failure, device malfunction, please check.", + "scheduled_cleaning_failed_full": "Scheduled cleaning failed, waste collection bin is full, please empty promptly", + "scheduled_cleaning_failed_hall_t": "Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "scheduled_cleaning_failed_falldown": "Scheduled cleaning failure, the litter box has been knocked down, please check.", + "scheduled_cleaning_failed_other": "Scheduled cleaning failure, device malfunction, please check.", + "manual_cleaning_failed_full": "Manual cleaning failed, waste collection bin is full, please empty promptly", + "manual_cleaning_failed_hall_t": "Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "manual_cleaning_failed_falldown": "Manual cleaning failure, the litter box has been knocked down, please check.", + "manual_cleaning_failed_other": "Manual cleaning failure, device malfunction, please check.", + "auto_cleaning_canceled": "Automatic cleaning canceled, device in operation", + "periodic_cleaning_canceled": "Periodic cleaning canceled, device in operation", + "manual_cleaning_canceled": "Manual cleaning canceled, device in operation", + "auto_cleaning_failed_maintenance": "Automatic cleaning failed, the device is in maintenance mode", + "periodic_cleaning_failed_maintenance": "Periodically cleaning failed, the device is in maintenance mode", + "auto_cleaning_canceled_kitten": "Kitten mode is enabled, auto cleaning is canceled", + "periodic_cleaning_canceled_kitten": "Kitten mode is enabled, periodically cleaning is canceled", + "litter_empty_completed": "Cat litter empty completed", + "litter_empty_terminated": "Cat litter empty terminated", + "litter_empty_failed_full": "Cat litter empty failed, waste collection bin is full, please empty promptly", + "litter_empty_failed_hall_t": "Cat litter empty failure, the litter box's cupper cover is not placed properly, please check", + "litter_empty_failed_falldown": "Cat litter empty failure, the litter box has been knocked down, please check", + "litter_empty_failed_other": "Cat litter empty failure, device malfunction, please check.", + "reset_completed": "Device reset completed", + "reset_terminated": "Device reset terminated", + "reset_failed_full": "Device reset failed, waste collection bin is full, please empty promptly", + "reset_failed_hall_t": "Device reset failure, the litter box's cupper cover is not placed properly, please check", + "reset_failed_falldown": "Device reset failure, the litter box has been knocked down, please check.", + "reset_failed_other": "Device reset failure, device malfunction, please check.", + "maintenance_mode": "Maintenance mode", + "deodorant_finished": "Deodorant finished", + "periodic_odor_completed": "Periodic odor removal completed", + "manual_odor_completed": "Manual odor removal completed", + "auto_odor_terminated": "Automatic odor removal has been terminated.", + "periodic_odor_terminated": "Periodic odor removal terminated.", + "manual_odor_terminated": "Manual odor removal terminated.", + "auto_odor_failed": "Automatic odor removal failed, odor eliminator error", + "periodic_odor_failed": "Periodic odor removal failure, odor eliminator malfunction", + "manual_odor_failed": "Manual odor removal failure, odor eliminator malfunction", + "auto_odor_canceled": "Automatic odor removal has been canceled, the device is running.", + "periodic_odor_canceled": "Periodic odor removal canceled. Litter Box is working.", + "manual_odor_canceled": "Manual odor removal canceled. Litter Box is working.", + "auto_odor_failed_device": "Automatic odor removal failed, no smart spray is connected.", + "periodic_odor_failed_device": "Periodic odor removal failed. Odor Removal Device disconnected.", + "manual_odor_failed_device": "Manual odor removal failed. Odor Removal Device disconnected.", + "auto_odor_failed_batt": "Automatic odor removal failed, please confirm that the battery of smart spray is sufficient.", + "periodic_odor_failed_batt": "Periodic odor removal failed. Please make sure the Odor Removal Device has sufficient battery.", + "manual_odor_failed_batt": "Manual odor removal failed. Please make sure the Odor Removal Device has sufficient battery.", + "auto_odor_failed_low_batt": "Automatic odor removal failed, battery is low.", + "periodic_odor_failed_low_batt": "Periodic odor removal failed. Odor Removal Device battery low.", + "manual_odor_failed_low_batt": "Manual odor removal failed. Odor Removal Device battery low.", + "cat_stopped_odor": "Your cat is using the litter box, deodorization has been canceled", + "light_on": "The light is ON", + "light_already_on": "The light is on. There is no need to turn on again.", + "light_malfunc": "Failing to turn on the light. Device malfunction, please check.", + "light_no_device": "Failing to turn on the light. Please bind the odor removal device first.", + "light_batt_cap": "Failing to turn on the light. Please check the battery capacity of the odor removal device.", + "light_low_batt": "Failing to turn on the light. Low battery capacity of odor removal device." + }, + "state_attributes": { + "sub_events": { + "state": { + "no_events_yet": "No events yet", + "event_type_unknown": "Event type unknown", + "cleaning_completed": "Cleaning completed", + "dumping_over": "Dumping Over", + "reset_over": "Reset over", + "spray_over": "Spray over", + "light_over": "Light over", + "pet_out": "Pet out", + "auto_cleaning_completed": "Auto cleaning completed", + "periodic_cleaning_completed": "Periodic cleaning completed", + "manual_cleaning_completed": "Manual cleaning completed", + "auto_cleaning_terminated": "Automatic cleaning terminated", + "periodic_cleaning_terminated": "Periodic cleaning terminated", + "manual_cleaning_terminated": "Manual cleaning terminated", + "auto_cleaning_failed_full": "Automatic cleaning failed, waste collection bin is full, please empty promptly", + "auto_cleaning_failed_hall_t": "Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "auto_cleaning_failed_falldown": "Automatic cleaning failure, the litter box has been knocked down, please check.", + "auto_cleaning_failed_other": "Automatic cleaning failure, device malfunction, please check.", + "scheduled_cleaning_failed_full": "Scheduled cleaning failed, waste collection bin is full, please empty promptly", + "scheduled_cleaning_failed_hall_t": "Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "scheduled_cleaning_failed_falldown": "Scheduled cleaning failure, the litter box has been knocked down, please check.", + "scheduled_cleaning_failed_other": "Scheduled cleaning failure, device malfunction, please check.", + "manual_cleaning_failed_full": "Manual cleaning failed, waste collection bin is full, please empty promptly", + "manual_cleaning_failed_hall_t": "Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "manual_cleaning_failed_falldown": "Manual cleaning failure, the litter box has been knocked down, please check.", + "manual_cleaning_failed_other": "Manual cleaning failure, device malfunction, please check.", + "auto_cleaning_canceled": "Automatic cleaning canceled, device in operation", + "periodic_cleaning_canceled": "Periodic cleaning canceled, device in operation", + "manual_cleaning_canceled": "Manual cleaning canceled, device in operation", + "auto_cleaning_failed_maintenance": "Automatic cleaning failed, the device is in maintenance mode", + "periodic_cleaning_failed_maintenance": "Periodically cleaning failed, the device is in maintenance mode", + "auto_cleaning_canceled_kitten": "Kitten mode is enabled, auto cleaning is canceled", + "periodic_cleaning_canceled_kitten": "Kitten mode is enabled, periodically cleaning is canceled", + "litter_empty_completed": "Cat litter empty completed", + "litter_empty_terminated": "Cat litter empty terminated", + "litter_empty_failed_full": "Cat litter empty failed, waste collection bin is full, please empty promptly", + "litter_empty_failed_hall_t": "Cat litter empty failure, the litter box's cupper cover is not placed properly, please check", + "litter_empty_failed_falldown": "Cat litter empty failure, the litter box has been knocked down, please check", + "litter_empty_failed_other": "Cat litter empty failure, device malfunction, please check.", + "reset_completed": "Device reset completed", + "reset_terminated": "Device reset terminated", + "reset_failed_full": "Device reset failed, waste collection bin is full, please empty promptly", + "reset_failed_hall_t": "Device reset failure, the litter box's cupper cover is not placed properly, please check", + "reset_failed_falldown": "Device reset failure, the litter box has been knocked down, please check.", + "reset_failed_other": "Device reset failure, device malfunction, please check.", + "maintenance_mode": "Maintenance mode", + "deodorant_finished": "Deodorant finished", + "periodic_odor_completed": "Periodic odor removal completed", + "manual_odor_completed": "Manual odor removal completed", + "auto_odor_terminated": "Automatic odor removal has been terminated.", + "periodic_odor_terminated": "Periodic odor removal terminated.", + "manual_odor_terminated": "Manual odor removal terminated.", + "auto_odor_failed": "Automatic odor removal failed, odor eliminator error", + "periodic_odor_failed": "Periodic odor removal failure, odor eliminator malfunction", + "manual_odor_failed": "Manual odor removal failure, odor eliminator malfunction", + "auto_odor_canceled": "Automatic odor removal has been canceled, the device is running.", + "periodic_odor_canceled": "Periodic odor removal canceled. Litter Box is working.", + "manual_odor_canceled": "Manual odor removal canceled. Litter Box is working.", + "auto_odor_failed_device": "Automatic odor removal failed, no smart spray is connected.", + "periodic_odor_failed_device": "Periodic odor removal failed. Odor Removal Device disconnected.", + "manual_odor_failed_device": "Manual odor removal failed. Odor Removal Device disconnected.", + "auto_odor_failed_batt": "Automatic odor removal failed, please confirm that the battery of smart spray is sufficient.", + "periodic_odor_failed_batt": "Periodic odor removal failed. Please make sure the Odor Removal Device has sufficient battery.", + "manual_odor_failed_batt": "Manual odor removal failed. Please make sure the Odor Removal Device has sufficient battery.", + "auto_odor_failed_low_batt": "Automatic odor removal failed, battery is low.", + "periodic_odor_failed_low_batt": "Periodic odor removal failed. Odor Removal Device battery low.", + "manual_odor_failed_low_batt": "Manual odor removal failed. Odor Removal Device battery low.", + "cat_stopped_odor": "Your cat is using the litter box, deodorization has been canceled", + "light_on": "The light is ON", + "light_already_on": "The light is on. There is no need to turn on again.", + "light_malfunc": "Failing to turn on the light. Device malfunction, please check.", + "light_no_device": "Failing to turn on the light. Please bind the odor removal device first.", + "light_batt_cap": "Failing to turn on the light. Please check the battery capacity of the odor removal device.", + "light_low_batt": "Failing to turn on the light. Low battery capacity of odor removal device.", + "no_sub_events": "No associated sub events" + } + } + } + }, + "max_work_state": { + "name": "State", + "state": { + "cleaning_litter_box": "Cleaning litter box", + "cleaning_litter_box_paused": "Litter box cleaning paused", + "cleaning_paused_pet_entered": "Litter box cleaning paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "cleaning_paused_system_error": "Litter box cleaning paused: System error, operation paused", + "cleaning_paused_pet_approach": "Litter box cleaning paused: Your cat is approaching, operation paused", + "cleaning_paused_pet_using": "Litter box cleaning paused: Your cat is using the device, operation paused", + "resetting_device": "Resetting device", + "litter_box_paused": "Litter box paused", + "paused_pet_entered": "Litter box paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "paused_system_error": "Litter box paused: System error, operation paused", + "paused_pet_approach": "Litter box paused: Your cat is approaching, operation paused", + "paused_pet_using": "Litter box paused: Your cat is using the device, operation paused", + "dumping_litter": "Dumping cat litter", + "dumping_litter_paused": "Dumping cat litter paused", + "dumping_paused_pet_entered": "Dumping cat litter paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "dumping_paused_system_error": "Dumping cat litter paused: System error, operation paused", + "dumping_paused_pet_approach": "Dumping cat litter paused: Your cat is approaching, operation paused", + "dumping_paused_pet_using": "Dumping cat litter paused: Your cat is using the device, operation paused", + "resetting": "Resetting", + "leveling": "Leveling cat litter, please wait.", + "calibrating": "Calibrating litter box, please wait.", + "maintenance_mode": "In maintenance mode", + "maintenance_paused_pet_entered": "Maintenance mode paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "maintenance_paused_cover": "Maintenance mode paused: The top cover is not installed, operation paused", + "maintenance_paused_system_error": "Maintenance mode paused: System error, operation paused", + "maintenance_paused_pet_approach": "Maintenance mode paused: Your cat is approaching, operation paused", + "maintenance_paused_pet_using": "Maintenance mode paused: Your cat is using the device, operation paused", + "maintenance_paused": "Maintenance mode paused", + "exit_maintenance": "Exiting maintenance mode", + "maintenance_exit_paused_pet_entered": "Maintenance mode exiting paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "maintenance_exit_paused_cover": "Maintenance mode exiting paused: The top cover is not installed, operation paused", + "maintenance_exit_paused_system_error": "Maintenance mode exiting paused: System error, operation paused", + "maintenance_exit_paused_pet_approach": "Maintenance mode exiting paused: Your cat is approaching, operation paused", + "maintenance_exit_paused_pet_using": "Maintenance mode exiting paused: Your cat is using the device, operation paused", + "maintenance_exit_paused": "Maintenance mode exiting paused", + "idle": "Idle" + } + } + }, + "switch": { + "light": { + "name": "Light" + }, + "power": { + "name": "Power" + }, + "do_not_disturb": { + "name": "Do not disturb" + }, + "indicator_light": { + "name": "Indicator light" + }, + "child_lock": { + "name": "Child lock" + }, + "food_shortage_alarm": { + "name": "Food shortage alarm" + }, + "dispense_tone": { + "name": "Dispense tone" + }, + "voice_with_dispense": { + "name": "Voice with dispense" + }, + "surplus_control": { + "name": "Surplus control" + }, + "system_notification_sound": { + "name": "System notification sound" + }, + "auto_odor_removal": { + "name": "Auto odor removal" + }, + "auto_cleaning": { + "name": "Auto cleaning" + }, + "avoid_repeat_cleaning": { + "name": "Avoid repeat cleaning" + }, + "periodic_cleaning": { + "name": "Periodic cleaning" + }, + "periodic_odor_removal": { + "name": "Periodic odor removal" + }, + "kitten_mode": { + "name": "Kitten mode" + }, + "display": { + "name": "Display" + }, + "light_weight_cleaning_disabled": { + "name": "Light weight cleaning disabled" + }, + "cont_rotation": { + "name": "Continuous rotation" + }, + "deep_cleaning": { + "name": "Deep cleaning" + }, + "deep_deodor": { + "name": "Deep deodorization" + } + } } } diff --git a/custom_components/petkit/switch.py b/custom_components/petkit/switch.py index 289b809..68bfdfe 100644 --- a/custom_components/petkit/switch.py +++ b/custom_components/petkit/switch.py @@ -63,20 +63,35 @@ async def async_setup_entry( # Litter boxes for lb_id, lb_data in coordinator.data.litter_boxes.items(): - # Pura X + # Pura X & Pura MAX switches.extend(( - LBAutoOdor(coordinator, lb_id), LBAutoClean(coordinator, lb_id), LBAvoidRepeat(coordinator, lb_id), LBDoNotDisturb(coordinator, lb_id), LBPeriodicCleaning(coordinator, lb_id), - LBPeriodicOdor(coordinator, lb_id), LBKittenMode(coordinator, lb_id), LBDisplay(coordinator, lb_id), LBChildLock(coordinator, lb_id), LBLightWeight(coordinator, lb_id), - LBPower(coordinator, lb_id), + LBPower(coordinator, lb_id) )) + # Pura X & Pura MAX with Pura Air + if (lb_data.type == 't3') or ('k3Device' in lb_data.device_detail): + switches.extend(( + LBAutoOdor(coordinator, lb_id), + LBPeriodicOdor(coordinator, lb_id) + )) + # Pura MAX + if lb_data.type == 't4': + switches.extend(( + LBContRotation(coordinator, lb_id), + LBDeepCleaning(coordinator, lb_id) + )) + # Pura MAX with Pura Air + if 'k3Device' in lb_data.device_detail: + switches.append( + LBDeepDeodor(coordinator, lb_id) + ) async_add_entities(switches) @@ -112,18 +127,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_light' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Light" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "light" + @property def icon(self) -> str: """Set icon.""" @@ -156,7 +171,7 @@ async def async_turn_on(self, **kwargs) -> None: """Turn light on.""" try: - await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.LIGHTON) + await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.LIGHT_ON) except BluetoothError: raise PetKitBluetoothError(f'Bluetooth connection to {self.wf_data.data["name"]} failed. Please try turning on the light again.') else: @@ -169,7 +184,7 @@ async def async_turn_off(self, **kwargs) -> None: """Turn light off.""" try: - await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.LIGHTOFF) + await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.LIGHT_OFF) except BluetoothError: raise PetKitBluetoothError(f'Bluetooth connection to {self.wf_data.data["name"]} failed. Please try turning off the light again.') else: @@ -209,18 +224,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_power' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Power" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "power" + @property def icon(self) -> str: """Set icon.""" @@ -319,18 +334,18 @@ def unique_id(self) -> str: return str(self.wf_data.id) + '_disturb' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Do not disturb" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "do_not_disturb" + @property def icon(self) -> str: """Set icon.""" @@ -369,7 +384,7 @@ async def async_turn_on(self, **kwargs) -> None: """Turn DND on.""" try: - await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.DONOTDISTURB) + await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.DO_NOT_DISTURB) except BluetoothError: raise PetKitBluetoothError(f'Bluetooth connection to {self.wf_data.data["name"]} failed. Please try turning on Do Not Disturb again.') else: @@ -382,7 +397,7 @@ async def async_turn_off(self, **kwargs) -> None: """Turn DND off.""" try: - await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.DONOTDISTURBOFF) + await self.coordinator.client.control_water_fountain(self.wf_data, W5Command.DO_NOT_DISTURB_OFF) except BluetoothError: raise PetKitBluetoothError(f'Bluetooth connection to {self.wf_data.data["name"]} failed. Please try turning off Do Not Disturb again.') else: @@ -422,18 +437,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_indicator_light' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Indicator light" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "indicator_light" + @property def icon(self) -> str: """Set icon.""" @@ -462,9 +477,9 @@ async def async_turn_on(self, **kwargs) -> None: """Turn indicator light on.""" if self.feeder_data.type == 'feedermini': - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.MINIINDICATORLIGHT, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.MINI_INDICATOR_LIGHT, 1) else: - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.INDICATORLIGHT, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.INDICATOR_LIGHT, 1) self.feeder_data.data['settings']['lightMode'] = 1 self.async_write_ha_state() @@ -474,9 +489,9 @@ async def async_turn_off(self, **kwargs) -> None: """Turn indicator light off.""" if self.feeder_data.type == 'feedermini': - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.MINIINDICATORLIGHT, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.MINI_INDICATOR_LIGHT, 0) else: - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.INDICATORLIGHT, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.INDICATOR_LIGHT, 0) self.feeder_data.data['settings']['lightMode'] = 0 self.async_write_ha_state() @@ -513,18 +528,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_child_lock' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Child lock" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "child_lock" + @property def icon(self) -> str: """Set icon.""" @@ -559,9 +574,9 @@ async def async_turn_on(self, **kwargs) -> None: """Turn child lock on.""" if self.feeder_data.type == 'feedermini': - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.MINICHILDLOCK, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.MINI_CHILD_LOCK, 1) else: - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.CHILDLOCK, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.CHILD_LOCK, 1) self.feeder_data.data['settings']['manualLock'] = 1 self.async_write_ha_state() @@ -571,9 +586,9 @@ async def async_turn_off(self, **kwargs) -> None: """Turn child lock off.""" if self.feeder_data.type == 'feedermini': - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.MINICHILDLOCK, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.MINI_CHILD_LOCK, 0) else: - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.CHILDLOCK, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.CHILD_LOCK, 0) self.feeder_data.data['settings']['manualLock'] = 0 self.async_write_ha_state() @@ -610,18 +625,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_food_shortage_alarm' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Food shortage alarm" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "food_shortage_alarm" + @property def icon(self) -> str: """Set icon.""" @@ -655,7 +670,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn food shortage alarm on.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SHORTAGEALARM, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SHORTAGE_ALARM, 1) self.feeder_data.data['settings']['foodWarn'] = 1 self.async_write_ha_state() await self.coordinator.async_request_refresh() @@ -663,7 +678,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn food shortage alarm off.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SHORTAGEALARM, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SHORTAGE_ALARM, 0) self.feeder_data.data['settings']['foodWarn'] = 0 self.async_write_ha_state() await self.coordinator.async_request_refresh() @@ -699,18 +714,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_dispense_tone' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Dispense tone" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "dispense_tone" + @property def icon(self) -> str: """Set icon.""" @@ -744,7 +759,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn food shortage alarm on.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.DISPENSETONE, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.DISPENSE_TONE, 1) self.feeder_data.data['settings']['feedSound'] = 1 self.async_write_ha_state() await self.coordinator.async_request_refresh() @@ -752,7 +767,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn food shortage alarm off.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.DISPENSETONE, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.DISPENSE_TONE, 0) self.feeder_data.data['settings']['feedSound'] = 0 self.async_write_ha_state() await self.coordinator.async_request_refresh() @@ -789,18 +804,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_voice_dispense' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Voice with dispense" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "voice_with_dispense" + @property def icon(self) -> str: """Set icon.""" @@ -834,7 +849,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn voice with dispense on.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SOUNDENABLE, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SOUND_ENABLE, 1) self.feeder_data.data['settings']['soundEnable'] = 1 self.async_write_ha_state() @@ -843,7 +858,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn voice with dispense off.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SOUNDENABLE, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SOUND_ENABLE, 0) self.feeder_data.data['settings']['soundEnable'] = 0 self.async_write_ha_state() @@ -881,18 +896,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_do_not_disturb' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Do not disturb" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "do_not_disturb" + @property def icon(self) -> str: """Set icon.""" @@ -926,7 +941,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn DND on.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.DONOTDISTURB, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.DO_NOT_DISTURB, 1) self.feeder_data.data['settings']['disturbMode'] = 1 self.async_write_ha_state() @@ -935,7 +950,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn DND off.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.DONOTDISTURB, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.DO_NOT_DISTURB, 0) self.feeder_data.data['settings']['disturbMode'] = 0 self.async_write_ha_state() @@ -973,18 +988,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_surplus_control' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Surplus control" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "surplus_control" + @property def icon(self) -> str: """Set icon.""" @@ -1018,7 +1033,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn surplus control on.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SURPLUSCONTROL, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SURPLUS_CONTROL, 1) self.feeder_data.data['settings']['surplusControl'] = 1 self.async_write_ha_state() @@ -1027,7 +1042,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn surplus control off.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SURPLUSCONTROL, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SURPLUS_CONTROL, 0) self.feeder_data.data['settings']['surplusControl'] = 0 self.async_write_ha_state() @@ -1065,18 +1080,18 @@ def unique_id(self) -> str: return str(self.feeder_data.id) + '_system_notification' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "System notification sound" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "system_notification_sound" + @property def icon(self) -> str: """Set icon.""" @@ -1110,7 +1125,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn system notification on.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SYSTEMSOUND, 1) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SYSTEM_SOUND, 1) self.feeder_data.data['settings']['systemSoundEnable'] = 1 self.async_write_ha_state() @@ -1119,7 +1134,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn system notification off.""" - await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SYSTEMSOUND, 0) + await self.coordinator.client.update_feeder_settings(self.feeder_data, FeederSetting.SYSTEM_SOUND, 0) self.feeder_data.data['settings']['systemSoundEnable'] = 0 self.async_write_ha_state() @@ -1157,18 +1172,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_auto_odor' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Auto odor removal" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "auto_odor_removal" + @property def icon(self) -> str: """Set icon.""" @@ -1195,14 +1210,21 @@ def available(self) -> bool: """Only make available if device is online.""" if self.lb_data.device_detail['state']['pim'] != 0: - return True + # Make Sure Pura MAX has Pura Air associated with it + if self.lb_data.type == 't4': + if 'k3Device' in self.lb_data.device_detail: + return True + else: + return False + else: + return True else: return False async def async_turn_on(self, **kwargs) -> None: """Turn auto odor removal on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AUTOODOR, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AUTO_ODOR, 1) self.lb_data.device_detail['settings']['autoRefresh'] = 1 self.async_write_ha_state() @@ -1211,7 +1233,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn auto odor removal off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AUTOODOR, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AUTO_ODOR, 0) self.lb_data.device_detail['settings']['autoRefresh'] = 0 self.async_write_ha_state() @@ -1249,18 +1271,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_auto_clean' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Auto cleaning" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "auto_cleaning" + @property def icon(self) -> str: """Set icon.""" @@ -1291,7 +1313,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn auto cleaning on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AUTOCLEAN, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AUTO_CLEAN, 1) self.lb_data.device_detail['settings']['autoWork'] = 1 self.async_write_ha_state() @@ -1300,7 +1322,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn auto cleaning off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AUTOCLEAN, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AUTO_CLEAN, 0) self.lb_data.device_detail['settings']['autoWork'] = 0 self.async_write_ha_state() @@ -1338,18 +1360,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_avoid_repeat' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Avoid repeat cleaning" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "avoid_repeat_cleaning" + @property def icon(self) -> str: """Set icon.""" @@ -1387,7 +1409,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn avoid repeat cleaning on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AVOIDREPEATCLEAN, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AVOID_REPEAT_CLEAN, 1) self.lb_data.device_detail['settings']['avoidRepeat'] = 1 self.async_write_ha_state() @@ -1396,7 +1418,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn avoid repeat cleaning off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AVOIDREPEATCLEAN, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.AVOID_REPEAT_CLEAN, 0) self.lb_data.device_detail['settings']['avoidRepeat'] = 0 self.async_write_ha_state() @@ -1434,18 +1456,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_dnd' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Do not disturb" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "do_not_disturb" + @property def icon(self) -> str: """Set icon.""" @@ -1471,7 +1493,7 @@ def is_on(self) -> bool: def available(self) -> bool: """Only make available if device is online.""" - if (self.lb_data.device_detail['state']['pim'] != 0): + if self.lb_data.device_detail['state']['pim'] != 0: return True else: return False @@ -1479,7 +1501,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn dnd on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DONOTDISTURB, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DO_NOT_DISTURB, 1) self.lb_data.device_detail['settings']['disturbMode'] = 1 self.async_write_ha_state() @@ -1488,7 +1510,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn dnd off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DONOTDISTURB, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DO_NOT_DISTURB, 0) self.lb_data.device_detail['settings']['disturbMode'] = 0 self.async_write_ha_state() @@ -1526,18 +1548,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_periodic_cleaning' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Periodic cleaning" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "periodic_cleaning" + @property def icon(self) -> str: """Set icon.""" @@ -1571,7 +1593,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn periodic cleaning on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.PERIODICCLEAN, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.PERIODIC_CLEAN, 1) self.lb_data.device_detail['settings']['fixedTimeClear'] = 1 self.async_write_ha_state() @@ -1580,7 +1602,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn periodic cleaning off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.PERIODICCLEAN, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.PERIODIC_CLEAN, 0) self.lb_data.device_detail['settings']['fixedTimeClear'] = 0 self.async_write_ha_state() @@ -1618,18 +1640,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_periodic_odor' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Periodic odor removal" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "periodic_odor_removal" + @property def icon(self) -> str: """Set icon.""" @@ -1655,15 +1677,22 @@ def is_on(self) -> bool: def available(self) -> bool: """Only make available if device is online.""" - if (self.lb_data.device_detail['state']['pim'] != 0): - return True + if self.lb_data.device_detail['state']['pim'] != 0: + # Make sure Pura MAX has associated Pura Air + if self.lb_data.type == 't4': + if 'k3Device' in self.lb_data.device_detail: + return True + else: + return False + else: + return True else: return False async def async_turn_on(self, **kwargs) -> None: """Turn periodic odor removal on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.PERIODICODOR, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.PERIODIC_ODOR, 1) self.lb_data.device_detail['settings']['fixedTimeRefresh'] = 1 self.async_write_ha_state() @@ -1672,7 +1701,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn periodic odor removal off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.PERIODICODOR, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.PERIODIC_ODOR, 0) self.lb_data.device_detail['settings']['fixedTimeRefresh'] = 0 self.async_write_ha_state() @@ -1710,18 +1739,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_kitten_mode' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Kitten mode" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "kitten_mode" + @property def icon(self) -> str: """Set icon.""" @@ -1744,7 +1773,7 @@ def is_on(self) -> bool: def available(self) -> bool: """Only make available if device is online.""" - if (self.lb_data.device_detail['state']['pim'] != 0): + if self.lb_data.device_detail['state']['pim'] != 0: return True else: return False @@ -1752,7 +1781,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn kitten mode on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.KITTENMODE, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.KITTEN_MODE, 1) self.lb_data.device_detail['settings']['kitten'] = 1 self.async_write_ha_state() @@ -1761,7 +1790,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn kitten mode off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.KITTENMODE, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.KITTEN_MODE, 0) self.lb_data.device_detail['settings']['kitten'] = 0 self.async_write_ha_state() @@ -1799,18 +1828,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_display' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Display" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "display" + @property def icon(self) -> str: """Set icon.""" @@ -1836,7 +1865,7 @@ def is_on(self) -> bool: def available(self) -> bool: """Only make available if device is online.""" - if (self.lb_data.device_detail['state']['pim'] != 0): + if self.lb_data.device_detail['state']['pim'] != 0: return True else: return False @@ -1891,18 +1920,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_child_lock' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Child lock" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "child_lock" + @property def icon(self) -> str: """Set icon.""" @@ -1928,7 +1957,7 @@ def is_on(self) -> bool: def available(self) -> bool: """Only make available if device is online.""" - if (self.lb_data.device_detail['state']['pim'] != 0): + if self.lb_data.device_detail['state']['pim'] != 0: return True else: return False @@ -1936,7 +1965,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn child lock on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.CHILDLOCK, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.CHILD_LOCK, 1) self.lb_data.device_detail['settings']['manualLock'] = 1 self.async_write_ha_state() @@ -1945,7 +1974,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn child lock off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.CHILDLOCK, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.CHILD_LOCK, 0) self.lb_data.device_detail['settings']['manualLock'] = 0 self.async_write_ha_state() @@ -1983,18 +2012,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_light_weight' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Light weight cleaning disabled" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "light_weight_cleaning_disabled" + @property def icon(self) -> str: """Set icon.""" @@ -2021,9 +2050,9 @@ def available(self) -> bool: auto_clean = self.lb_data.device_detail['settings']['autoWork'] == 1 avoid_repeat = self.lb_data.device_detail['settings']['avoidRepeat'] == 1 - if (self.lb_data.device_detail['state']['pim'] != 0): + if self.lb_data.device_detail['state']['pim'] != 0: # Kitten mode must be off and auto cleaning and avoid repeat must be on - if (kitten_mode_off and auto_clean and avoid_repeat): + if kitten_mode_off and auto_clean and avoid_repeat: return True else: return False @@ -2033,7 +2062,7 @@ def available(self) -> bool: async def async_turn_on(self, **kwargs) -> None: """Turn light weight disabler on.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DISABLELIGHTWEIGHT, 1) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DISABLE_LIGHT_WEIGHT, 1) self.lb_data.device_detail['settings']['underweight'] = 1 self.async_write_ha_state() @@ -2042,7 +2071,7 @@ async def async_turn_on(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None: """Turn light weight disabler off.""" - await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DISABLELIGHTWEIGHT, 0) + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DISABLE_LIGHT_WEIGHT, 0) self.lb_data.device_detail['settings']['underweight'] = 0 self.async_write_ha_state() @@ -2080,18 +2109,18 @@ def unique_id(self) -> str: return str(self.lb_data.id) + '_power' - @property - def name(self) -> str: - """Return name of the entity.""" - - return "Power" - @property def has_entity_name(self) -> bool: """Indicate that entity has name defined.""" return True + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "power" + @property def icon(self) -> str: """Set icon.""" @@ -2111,7 +2140,7 @@ def is_on(self) -> bool: def available(self) -> bool: """Only make available if device is online.""" - if (self.lb_data.device_detail['state']['pim'] != 0): + if self.lb_data.device_detail['state']['pim'] != 0: return True else: return False @@ -2133,3 +2162,274 @@ async def async_turn_off(self, **kwargs) -> None: self.lb_data.device_detail['state']['power'] = 0 self.async_write_ha_state() await self.coordinator.async_request_refresh() + + +class LBContRotation(CoordinatorEntity, SwitchEntity): + """Representation of litter box continuous rotation setting.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_cont_rotation' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "cont_rotation" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:rotate-3d-variant' + + @property + def entity_category(self) -> EntityCategory: + """Set category to config.""" + + return EntityCategory.CONFIG + + @property + def is_on(self) -> bool: + """Determine if continuous rotation is on.""" + + return self.lb_data.device_detail['settings']['downpos'] == 1 + + @property + def available(self) -> bool: + """Only make available if device is online.""" + + if self.lb_data.device_detail['state']['pim'] != 0: + return True + else: + return False + + async def async_turn_on(self, **kwargs) -> None: + """Turn continuous rotation on.""" + + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.CONT_ROTATION, 1) + + self.lb_data.device_detail['settings']['downpos'] = 1 + self.async_write_ha_state() + await self.coordinator.async_request_refresh() + + async def async_turn_off(self, **kwargs) -> None: + """Turn continuous rotation off.""" + + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.CONT_ROTATION, 0) + + self.lb_data.device_detail['settings']['downpos'] = 0 + self.async_write_ha_state() + await self.coordinator.async_request_refresh() + + +class LBDeepCleaning(CoordinatorEntity, SwitchEntity): + """Representation of litter box deep cleaning setting.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_deep_cleaning' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "deep_cleaning" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:vacuum' + + @property + def entity_category(self) -> EntityCategory: + """Set category to config.""" + + return EntityCategory.CONFIG + + @property + def is_on(self) -> bool: + """Determine if deep cleaning is on.""" + + return self.lb_data.device_detail['settings']['deepClean'] == 1 + + @property + def available(self) -> bool: + """Only make available if device is online.""" + + if self.lb_data.device_detail['state']['pim'] != 0: + return True + else: + return False + + async def async_turn_on(self, **kwargs) -> None: + """Turn deep cleaning on.""" + + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DEEP_CLEAN, 1) + + self.lb_data.device_detail['settings']['deepClean'] = 1 + self.async_write_ha_state() + await self.coordinator.async_request_refresh() + + async def async_turn_off(self, **kwargs) -> None: + """Turn deep cleaning off.""" + + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DEEP_CLEAN, 0) + + self.lb_data.device_detail['settings']['deepClean'] = 0 + self.async_write_ha_state() + await self.coordinator.async_request_refresh() + + +class LBDeepDeodor(CoordinatorEntity, SwitchEntity): + """Representation of litter box deep deodorization setting.""" + + def __init__(self, coordinator, lb_id): + super().__init__(coordinator) + self.lb_id = lb_id + + @property + def lb_data(self) -> LitterBox: + """Handle coordinator litter box data.""" + + return self.coordinator.data.litter_boxes[self.lb_id] + + @property + def device_info(self) -> dict[str, Any]: + """Return device registry information for this entity.""" + + return { + "identifiers": {(DOMAIN, self.lb_data.id)}, + "name": self.lb_data.device_detail['name'], + "manufacturer": "PetKit", + "model": LITTER_BOXES[self.lb_data.type], + "sw_version": f'{self.lb_data.device_detail["firmware"]}' + } + + @property + def unique_id(self) -> str: + """Sets unique ID for this entity.""" + + return str(self.lb_data.id) + '_deep_deodor' + + @property + def has_entity_name(self) -> bool: + """Indicate that entity has name defined.""" + + return True + + @property + def translation_key(self) -> str: + """Translation key for this entity.""" + + return "deep_deodor" + + @property + def icon(self) -> str: + """Set icon.""" + + return 'mdi:spray-bottle' + + @property + def entity_category(self) -> EntityCategory: + """Set category to config.""" + + return EntityCategory.CONFIG + + @property + def is_on(self) -> bool: + """Determine if deep deodorization is on.""" + + return self.lb_data.device_detail['settings']['deepRefresh'] == 1 + + @property + def available(self) -> bool: + """Only make available if device is online.""" + + if self.lb_data.device_detail['state']['pim'] != 0: + # Make sure Pura Air is still associated with litter box + if 'k3Device' in self.lb_data.device_detail: + return True + else: + return False + else: + return False + + async def async_turn_on(self, **kwargs) -> None: + """Turn deep deodorization on.""" + + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DEEP_REFRESH, 1) + + self.lb_data.device_detail['settings']['deepRefresh'] = 1 + self.async_write_ha_state() + await self.coordinator.async_request_refresh() + + async def async_turn_off(self, **kwargs) -> None: + """Turn deep deodorization off.""" + + await self.coordinator.client.update_litter_box_settings(self.lb_data, LitterBoxSetting.DEEP_REFRESH, 0) + + self.lb_data.device_detail['settings']['deepRefresh'] = 0 + self.async_write_ha_state() + await self.coordinator.async_request_refresh() diff --git a/custom_components/petkit/translations/en.json b/custom_components/petkit/translations/en.json index 02c7a63..477f558 100644 --- a/custom_components/petkit/translations/en.json +++ b/custom_components/petkit/translations/en.json @@ -7,7 +7,8 @@ "error": { "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication. Are your credentials correct?", - "no_devices": "No devices found on account" + "no_devices": "No devices found on account", + "server_busy": "PetKit servers are busy. Please try again later." }, "step": { "user": { @@ -25,5 +26,618 @@ "title": "Reauthenticate with your PetKit credentials" } } + }, + "entity": { + "binary_sensor": { + "water_level": { + "name": "Water level" + }, + "food_level": { + "name": "Food level" + }, + "battery_installed": { + "name": "Battery installed" + }, + "battery": { + "name": "Battery" + }, + "wastebin": { + "name": "Wastebin" + }, + "litter": { + "name": "Litter" + }, + "deodorizer": { + "name": "Deodorizer" + }, + "manually_paused": { + "name": "Manually paused" + }, + "pura_air_liquid": { + "name": "Pura Air liquid" + } + }, + "button": { + "reset_filter": { + "name": "Reset filter" + }, + "reset_desiccant": { + "name": "Reset desiccant" + }, + "cancel_manual_feed": { + "name": "Cancel manual feed" + }, + "call_pet": { + "name": "Call pet" + }, + "start_cleaning": { + "name": "Start/Resume cleaning" + }, + "pause_cleaning": { + "name": "Pause cleaning" + }, + "odor_removal": { + "name": "Odor removal" + }, + "reset_deodorizer": { + "name": "Reset deodorizer" + }, + "reset_pura_air_liquid": { + "name": "Reset Pura Air liquid" + }, + "n50_reset": { + "name": "Reset N50 odor eliminator" + }, + "light_on": { + "name": "Turn light ON" + }, + "start_maintenance": { + "name": "Start maintenance mode" + }, + "exit_maintenance": { + "name": "Exit maintenance mode" + }, + "pause_exit_maintenance": { + "name": "Pause exiting maintenance mode" + }, + "resume_exit_maintenance": { + "name": "Resume exiting maintenance mode" + }, + "dump_litter": { + "name": "Dump litter" + }, + "pause_dump_litter": { + "name": "Pause litter dumping" + }, + "resume_dump_litter": { + "name": "Resume litter dumping" + } + }, + "number": { + "set_weight": { + "name": "Set weight" + }, + "surplus": { + "name": "Surplus" + }, + "volume": { + "name": "Volume" + }, + "manual_feed": { + "name": "Manual feed" + }, + "cleaning_delay": { + "name": "Cleaning delay" + } + }, + "select": { + "light_brightness": { + "name": "Light brightness", + "state": { + "low": "Low", + "medium": "Medium", + "high": "High" + } + }, + "mode": { + "name": "Mode", + "state": { + "normal": "Normal", + "smart": "Smart" + } + }, + "manual_feed": { + "name": "Manual feed" + }, + "sound": { + "name": "Sound" + }, + "cleaning_interval": { + "name": "Cleaning interval" + }, + "litter_type": { + "name": "Litter type", + "state": { + "bentonite": "Bentonite", + "tofu": "Tofu", + "mixed": "Mixed" + } + } + }, + "sensor": { + "energy_usage": { + "name": "Energy usage" + }, + "last_data_update": { + "name": "Last data update" + }, + "filter": { + "name": "Filter" + }, + "purified_water_today": { + "name": "Purified water today" + }, + "feeder_status": { + "name": "Status", + "state": { + "offline": "Offline", + "normal": "Normal", + "on_batteries": "On Batteries" + } + }, + "desiccant_days_remaining": { + "name": "Desiccant days remaining" + }, + "battery_status": { + "name": "Battery status", + "state": { + "normal": "Normal", + "low": "Low" + } + }, + "dispensed": { + "name": "Dispensed" + }, + "planned": { + "name": "Planned" + }, + "planned_dispensed": { + "name": "Planned dispensed" + }, + "manually_dispensed": { + "name": "Manually dispensed" + }, + "times_dispensed": { + "name": "Times dispensed" + }, + "rssi": { + "name": "RSSI" + }, + "amount_eaten": { + "name": "Amount eaten" + }, + "times_eaten": { + "name": "Times eaten" + }, + "food_in_bowl": { + "name": "Food in bowl" + }, + "error": { + "name": "Error", + "state": { + "no_error": "No Error" + } + }, + "deodorizer_level": { + "name": "Deodorizer level" + }, + "litter_level": { + "name": "Litter level" + }, + "litter_weight": { + "name": "Litter weight" + }, + "times_used": { + "name": "Times used" + }, + "average_use": { + "name": "Average use" + }, + "total_use": { + "name": "Total use" + }, + "last_used_by": { + "name": "Last used by", + "state": { + "no_record_yet": "No record yet", + "unknown_pet": "Unknown pet" + } + }, + "last_event": { + "name": "Last event", + "state": { + "no_events_yet": "No events yet", + "event_type_unknown": "Event type unknown", + "cleaning_completed": "Cleaning completed", + "dumping_over": "Dumping Over", + "reset_over": "Reset over", + "spray_over": "Spray over", + "pet_out": "Pet out", + "auto_cleaning_completed": "Auto cleaning completed", + "periodic_cleaning_completed": "Periodic cleaning completed", + "manual_cleaning_completed": "Manual cleaning completed", + "auto_cleaning_terminated": "Automatic cleaning terminated", + "periodic_cleaning_terminated": "Periodic cleaning terminated", + "manual_cleaning_terminated": "Manual cleaning terminated", + "auto_cleaning_failed_full": "Automatic cleaning failed, waste collection bin is full, please empty promptly", + "auto_cleaning_failed_hall_l": "Automatic cleaning failure, the cylinder is not properly locked in place, please check", + "auto_cleaning_failed_hall_t": "Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "scheduled_cleaning_failed_full": "Scheduled cleaning failed, waste collection bin is full, please empty promptly", + "scheduled_cleaning_failed_hall_l": "Scheduled cleaning failure, the cylinder is not properly locked in place, please check", + "scheduled_cleaning_failed_hall_t": "Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "manual_cleaning_failed_full": "Manual cleaning failed, waste collection bin is full, please empty promptly", + "manual_cleaning_failed_hall_l": "Manual cleaning failure, the cylinder is not properly locked in place, please check", + "manual_cleaning_failed_hall_t": "Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "auto_cleaning_canceled": "Automatic cleaning canceled, device in operation", + "periodic_cleaning_canceled": "Periodic cleaning canceled, device in operation", + "manual_cleaning_canceled": "Manual cleaning canceled, device in operation", + "auto_cleaning_canceled_kitten": "Kitten mode is enabled, auto cleaning is canceled", + "periodic_cleaning_canceled_kitten": "Kitten mode is enabled, periodically cleaning is canceled", + "litter_empty_completed": "Cat litter empty completed", + "litter_empty_terminated": "Cat litter empty terminated", + "litter_empty_failed_full": "Cat litter empty failed, waste collection bin is full, please empty promptly", + "litter_empty_failed_hall_l": "Cat litter empty failure, the cylinder is not properly locked in place, please check", + "litter_empty_failed_hall_t": "Cat litter empty failure, the litter box's cupper cover is not placed properly, please check", + "reset_completed": "Device reset completed", + "reset_terminated": "Device reset terminated", + "reset_failed_full": "Device reset failed, waste collection bin is full, please empty promptly", + "reset_failed_hall_l": "Device reset failure, the cylinder is not properly locked in place, please check", + "reset_failed_hall_t": "Device reset failure, the litter box's cupper cover is not placed properly, please check", + "deodorant_finished": "Deodorant finished", + "periodic_odor_completed": "Periodic odor removal completed", + "manual_odor_completed": "Manual odor removal completed", + "deodorant_finished_liquid_lack": "Deodorant finished, not enough purifying liquid, please refill in time", + "periodic_odor_completed_liquid_lack": "Periodic odor removal completed, not enough purifying liquid, please refill in time", + "manual_odor_completed_liquid_lack": "Manual odor removal completed, not enough purifying liquid, please refill in time", + "auto_odor_failed": "Automatic odor removal failed, odor eliminator error", + "periodic_odor_failed": "Periodic odor removal failure, odor eliminator malfunction", + "manual_odor_failed": "Manual odor removal failure, odor eliminator malfunction" + }, + "state_attributes": { + "sub_events": { + "state": { + "no_events_yet": "No events yet", + "event_type_unknown": "Event type unknown", + "cleaning_completed": "Cleaning completed", + "dumping_over": "Dumping Over", + "reset_over": "Reset over", + "spray_over": "Spray over", + "pet_out": "Pet out", + "auto_cleaning_completed": "Auto cleaning completed", + "periodic_cleaning_completed": "Periodic cleaning completed", + "manual_cleaning_completed": "Manual cleaning completed", + "auto_cleaning_terminated": "Automatic cleaning terminated", + "periodic_cleaning_terminated": "Periodic cleaning terminated", + "manual_cleaning_terminated": "Manual cleaning terminated", + "auto_cleaning_failed_full": "Automatic cleaning failed, waste collection bin is full, please empty promptly", + "auto_cleaning_failed_hall_l": "Automatic cleaning failure, the cylinder is not properly locked in place, please check", + "auto_cleaning_failed_hall_t": "Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "scheduled_cleaning_failed_full": "Scheduled cleaning failed, waste collection bin is full, please empty promptly", + "scheduled_cleaning_failed_hall_l": "Scheduled cleaning failure, the cylinder is not properly locked in place, please check", + "scheduled_cleaning_failed_hall_t": "Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "manual_cleaning_failed_full": "Manual cleaning failed, waste collection bin is full, please empty promptly", + "manual_cleaning_failed_hall_l": "Manual cleaning failure, the cylinder is not properly locked in place, please check", + "manual_cleaning_failed_hall_t": "Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "auto_cleaning_canceled": "Automatic cleaning canceled, device in operation", + "periodic_cleaning_canceled": "Periodic cleaning canceled, device in operation", + "manual_cleaning_canceled": "Manual cleaning canceled, device in operation", + "auto_cleaning_canceled_kitten": "Kitten mode is enabled, auto cleaning is canceled", + "periodic_cleaning_canceled_kitten": "Kitten mode is enabled, periodically cleaning is canceled", + "litter_empty_completed": "Cat litter empty completed", + "litter_empty_terminated": "Cat litter empty terminated", + "litter_empty_failed_full": "Cat litter empty failed, waste collection bin is full, please empty promptly", + "litter_empty_failed_hall_l": "Cat litter empty failure, the cylinder is not properly locked in place, please check", + "litter_empty_failed_hall_t": "Cat litter empty failure, the litter box's cupper cover is not placed properly, please check", + "reset_completed": "Device reset completed", + "reset_terminated": "Device reset terminated", + "reset_failed_full": "Device reset failed, waste collection bin is full, please empty promptly", + "reset_failed_hall_l": "Device reset failure, the cylinder is not properly locked in place, please check", + "reset_failed_hall_t": "Device reset failure, the litter box's cupper cover is not placed properly, please check", + "deodorant_finished": "Deodorant finished", + "periodic_odor_completed": "Periodic odor removal completed", + "manual_odor_completed": "Manual odor removal completed", + "deodorant_finished_liquid_lack": "Deodorant finished, not enough purifying liquid, please refill in time", + "periodic_odor_completed_liquid_lack": "Periodic odor removal completed, not enough purifying liquid, please refill in time", + "manual_odor_completed_liquid_lack": "Manual odor removal completed, not enough purifying liquid, please refill in time", + "auto_odor_failed": "Automatic odor removal failed, odor eliminator error", + "periodic_odor_failed": "Periodic odor removal failure, odor eliminator malfunction", + "manual_odor_failed": "Manual odor removal failure, odor eliminator malfunction", + "no_sub_events": "No associated sub events" + } + } + } + }, + "latest_weight": { + "name": "Latest weight" + }, + "last_use_duration": { + "name": "Last use duration" + }, + "pura_air_battery": { + "name": "Pura Air battery" + }, + "pura_air_liquid": { + "name": "Pura Air liquid" + }, + "n50_odor_eliminator": { + "name": "N50 odor eliminator" + }, + "max_last_event": { + "name": "Last event", + "state": { + "no_events_yet": "No events yet", + "event_type_unknown": "Event type unknown", + "cleaning_completed": "Cleaning completed", + "dumping_over": "Dumping Over", + "reset_over": "Reset over", + "spray_over": "Spray over", + "light_over": "Light over", + "pet_out": "Pet out", + "auto_cleaning_completed": "Auto cleaning completed", + "periodic_cleaning_completed": "Periodic cleaning completed", + "manual_cleaning_completed": "Manual cleaning completed", + "auto_cleaning_terminated": "Automatic cleaning terminated", + "periodic_cleaning_terminated": "Periodic cleaning terminated", + "manual_cleaning_terminated": "Manual cleaning terminated", + "auto_cleaning_failed_full": "Automatic cleaning failed, waste collection bin is full, please empty promptly", + "auto_cleaning_failed_hall_t": "Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "auto_cleaning_failed_falldown": "Automatic cleaning failure, the litter box has been knocked down, please check.", + "auto_cleaning_failed_other": "Automatic cleaning failure, device malfunction, please check.", + "scheduled_cleaning_failed_full": "Scheduled cleaning failed, waste collection bin is full, please empty promptly", + "scheduled_cleaning_failed_hall_t": "Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "scheduled_cleaning_failed_falldown": "Scheduled cleaning failure, the litter box has been knocked down, please check.", + "scheduled_cleaning_failed_other": "Scheduled cleaning failure, device malfunction, please check.", + "manual_cleaning_failed_full": "Manual cleaning failed, waste collection bin is full, please empty promptly", + "manual_cleaning_failed_hall_t": "Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "manual_cleaning_failed_falldown": "Manual cleaning failure, the litter box has been knocked down, please check.", + "manual_cleaning_failed_other": "Manual cleaning failure, device malfunction, please check.", + "auto_cleaning_canceled": "Automatic cleaning canceled, device in operation", + "periodic_cleaning_canceled": "Periodic cleaning canceled, device in operation", + "manual_cleaning_canceled": "Manual cleaning canceled, device in operation", + "auto_cleaning_failed_maintenance": "Automatic cleaning failed, the device is in maintenance mode", + "periodic_cleaning_failed_maintenance": "Periodically cleaning failed, the device is in maintenance mode", + "auto_cleaning_canceled_kitten": "Kitten mode is enabled, auto cleaning is canceled", + "periodic_cleaning_canceled_kitten": "Kitten mode is enabled, periodically cleaning is canceled", + "litter_empty_completed": "Cat litter empty completed", + "litter_empty_terminated": "Cat litter empty terminated", + "litter_empty_failed_full": "Cat litter empty failed, waste collection bin is full, please empty promptly", + "litter_empty_failed_hall_t": "Cat litter empty failure, the litter box's cupper cover is not placed properly, please check", + "litter_empty_failed_falldown": "Cat litter empty failure, the litter box has been knocked down, please check", + "litter_empty_failed_other": "Cat litter empty failure, device malfunction, please check.", + "reset_completed": "Device reset completed", + "reset_terminated": "Device reset terminated", + "reset_failed_full": "Device reset failed, waste collection bin is full, please empty promptly", + "reset_failed_hall_t": "Device reset failure, the litter box's cupper cover is not placed properly, please check", + "reset_failed_falldown": "Device reset failure, the litter box has been knocked down, please check.", + "reset_failed_other": "Device reset failure, device malfunction, please check.", + "maintenance_mode": "Maintenance mode", + "deodorant_finished": "Deodorant finished", + "periodic_odor_completed": "Periodic odor removal completed", + "manual_odor_completed": "Manual odor removal completed", + "auto_odor_terminated": "Automatic odor removal has been terminated.", + "periodic_odor_terminated": "Periodic odor removal terminated.", + "manual_odor_terminated": "Manual odor removal terminated.", + "auto_odor_failed": "Automatic odor removal failed, odor eliminator error", + "periodic_odor_failed": "Periodic odor removal failure, odor eliminator malfunction", + "manual_odor_failed": "Manual odor removal failure, odor eliminator malfunction", + "auto_odor_canceled": "Automatic odor removal has been canceled, the device is running.", + "periodic_odor_canceled": "Periodic odor removal canceled. Litter Box is working.", + "manual_odor_canceled": "Manual odor removal canceled. Litter Box is working.", + "auto_odor_failed_device": "Automatic odor removal failed, no smart spray is connected.", + "periodic_odor_failed_device": "Periodic odor removal failed. Odor Removal Device disconnected.", + "manual_odor_failed_device": "Manual odor removal failed. Odor Removal Device disconnected.", + "auto_odor_failed_batt": "Automatic odor removal failed, please confirm that the battery of smart spray is sufficient.", + "periodic_odor_failed_batt": "Periodic odor removal failed. Please make sure the Odor Removal Device has sufficient battery.", + "manual_odor_failed_batt": "Manual odor removal failed. Please make sure the Odor Removal Device has sufficient battery.", + "auto_odor_failed_low_batt": "Automatic odor removal failed, battery is low.", + "periodic_odor_failed_low_batt": "Periodic odor removal failed. Odor Removal Device battery low.", + "manual_odor_failed_low_batt": "Manual odor removal failed. Odor Removal Device battery low.", + "cat_stopped_odor": "Your cat is using the litter box, deodorization has been canceled", + "light_on": "The light is ON", + "light_already_on": "The light is on. There is no need to turn on again.", + "light_malfunc": "Failing to turn on the light. Device malfunction, please check.", + "light_no_device": "Failing to turn on the light. Please bind the odor removal device first.", + "light_batt_cap": "Failing to turn on the light. Please check the battery capacity of the odor removal device.", + "light_low_batt": "Failing to turn on the light. Low battery capacity of odor removal device." + }, + "state_attributes": { + "sub_events": { + "state": { + "no_events_yet": "No events yet", + "event_type_unknown": "Event type unknown", + "cleaning_completed": "Cleaning completed", + "dumping_over": "Dumping Over", + "reset_over": "Reset over", + "spray_over": "Spray over", + "light_over": "Light over", + "pet_out": "Pet out", + "auto_cleaning_completed": "Auto cleaning completed", + "periodic_cleaning_completed": "Periodic cleaning completed", + "manual_cleaning_completed": "Manual cleaning completed", + "auto_cleaning_terminated": "Automatic cleaning terminated", + "periodic_cleaning_terminated": "Periodic cleaning terminated", + "manual_cleaning_terminated": "Manual cleaning terminated", + "auto_cleaning_failed_full": "Automatic cleaning failed, waste collection bin is full, please empty promptly", + "auto_cleaning_failed_hall_t": "Automatic cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "auto_cleaning_failed_falldown": "Automatic cleaning failure, the litter box has been knocked down, please check.", + "auto_cleaning_failed_other": "Automatic cleaning failure, device malfunction, please check.", + "scheduled_cleaning_failed_full": "Scheduled cleaning failed, waste collection bin is full, please empty promptly", + "scheduled_cleaning_failed_hall_t": "Scheduled cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "scheduled_cleaning_failed_falldown": "Scheduled cleaning failure, the litter box has been knocked down, please check.", + "scheduled_cleaning_failed_other": "Scheduled cleaning failure, device malfunction, please check.", + "manual_cleaning_failed_full": "Manual cleaning failed, waste collection bin is full, please empty promptly", + "manual_cleaning_failed_hall_t": "Manual cleaning failure, the litter box's upper cupper cover is not placed properly, please check", + "manual_cleaning_failed_falldown": "Manual cleaning failure, the litter box has been knocked down, please check.", + "manual_cleaning_failed_other": "Manual cleaning failure, device malfunction, please check.", + "auto_cleaning_canceled": "Automatic cleaning canceled, device in operation", + "periodic_cleaning_canceled": "Periodic cleaning canceled, device in operation", + "manual_cleaning_canceled": "Manual cleaning canceled, device in operation", + "auto_cleaning_failed_maintenance": "Automatic cleaning failed, the device is in maintenance mode", + "periodic_cleaning_failed_maintenance": "Periodically cleaning failed, the device is in maintenance mode", + "auto_cleaning_canceled_kitten": "Kitten mode is enabled, auto cleaning is canceled", + "periodic_cleaning_canceled_kitten": "Kitten mode is enabled, periodically cleaning is canceled", + "litter_empty_completed": "Cat litter empty completed", + "litter_empty_terminated": "Cat litter empty terminated", + "litter_empty_failed_full": "Cat litter empty failed, waste collection bin is full, please empty promptly", + "litter_empty_failed_hall_t": "Cat litter empty failure, the litter box's cupper cover is not placed properly, please check", + "litter_empty_failed_falldown": "Cat litter empty failure, the litter box has been knocked down, please check", + "litter_empty_failed_other": "Cat litter empty failure, device malfunction, please check.", + "reset_completed": "Device reset completed", + "reset_terminated": "Device reset terminated", + "reset_failed_full": "Device reset failed, waste collection bin is full, please empty promptly", + "reset_failed_hall_t": "Device reset failure, the litter box's cupper cover is not placed properly, please check", + "reset_failed_falldown": "Device reset failure, the litter box has been knocked down, please check.", + "reset_failed_other": "Device reset failure, device malfunction, please check.", + "maintenance_mode": "Maintenance mode", + "deodorant_finished": "Deodorant finished", + "periodic_odor_completed": "Periodic odor removal completed", + "manual_odor_completed": "Manual odor removal completed", + "auto_odor_terminated": "Automatic odor removal has been terminated.", + "periodic_odor_terminated": "Periodic odor removal terminated.", + "manual_odor_terminated": "Manual odor removal terminated.", + "auto_odor_failed": "Automatic odor removal failed, odor eliminator error", + "periodic_odor_failed": "Periodic odor removal failure, odor eliminator malfunction", + "manual_odor_failed": "Manual odor removal failure, odor eliminator malfunction", + "auto_odor_canceled": "Automatic odor removal has been canceled, the device is running.", + "periodic_odor_canceled": "Periodic odor removal canceled. Litter Box is working.", + "manual_odor_canceled": "Manual odor removal canceled. Litter Box is working.", + "auto_odor_failed_device": "Automatic odor removal failed, no smart spray is connected.", + "periodic_odor_failed_device": "Periodic odor removal failed. Odor Removal Device disconnected.", + "manual_odor_failed_device": "Manual odor removal failed. Odor Removal Device disconnected.", + "auto_odor_failed_batt": "Automatic odor removal failed, please confirm that the battery of smart spray is sufficient.", + "periodic_odor_failed_batt": "Periodic odor removal failed. Please make sure the Odor Removal Device has sufficient battery.", + "manual_odor_failed_batt": "Manual odor removal failed. Please make sure the Odor Removal Device has sufficient battery.", + "auto_odor_failed_low_batt": "Automatic odor removal failed, battery is low.", + "periodic_odor_failed_low_batt": "Periodic odor removal failed. Odor Removal Device battery low.", + "manual_odor_failed_low_batt": "Manual odor removal failed. Odor Removal Device battery low.", + "cat_stopped_odor": "Your cat is using the litter box, deodorization has been canceled", + "light_on": "The light is ON", + "light_already_on": "The light is on. There is no need to turn on again.", + "light_malfunc": "Failing to turn on the light. Device malfunction, please check.", + "light_no_device": "Failing to turn on the light. Please bind the odor removal device first.", + "light_batt_cap": "Failing to turn on the light. Please check the battery capacity of the odor removal device.", + "light_low_batt": "Failing to turn on the light. Low battery capacity of odor removal device.", + "no_sub_events": "No associated sub events" + } + } + } + }, + "max_work_state": { + "name": "State", + "state": { + "cleaning_litter_box": "Cleaning litter box", + "cleaning_litter_box_paused": "Litter box cleaning paused", + "cleaning_paused_pet_entered": "Litter box cleaning paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "cleaning_paused_system_error": "Litter box cleaning paused: System error, operation paused", + "cleaning_paused_pet_approach": "Litter box cleaning paused: Your cat is approaching, operation paused", + "cleaning_paused_pet_using": "Litter box cleaning paused: Your cat is using the device, operation paused", + "resetting_device": "Resetting device", + "litter_box_paused": "Litter box paused", + "paused_pet_entered": "Litter box paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "paused_system_error": "Litter box paused: System error, operation paused", + "paused_pet_approach": "Litter box paused: Your cat is approaching, operation paused", + "paused_pet_using": "Litter box paused: Your cat is using the device, operation paused", + "dumping_litter": "Dumping cat litter", + "dumping_litter_paused": "Dumping cat litter paused", + "dumping_paused_pet_entered": "Dumping cat litter paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "dumping_paused_system_error": "Dumping cat litter paused: System error, operation paused", + "dumping_paused_pet_approach": "Dumping cat litter paused: Your cat is approaching, operation paused", + "dumping_paused_pet_using": "Dumping cat litter paused: Your cat is using the device, operation paused", + "resetting": "Resetting", + "leveling": "Leveling cat litter, please wait.", + "calibrating": "Calibrating litter box, please wait.", + "maintenance_mode": "In maintenance mode", + "maintenance_paused_pet_entered": "Maintenance mode paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "maintenance_paused_cover": "Maintenance mode paused: The top cover is not installed, operation paused", + "maintenance_paused_system_error": "Maintenance mode paused: System error, operation paused", + "maintenance_paused_pet_approach": "Maintenance mode paused: Your cat is approaching, operation paused", + "maintenance_paused_pet_using": "Maintenance mode paused: Your cat is using the device, operation paused", + "maintenance_paused": "Maintenance mode paused", + "exit_maintenance": "Exiting maintenance mode", + "maintenance_exit_paused_pet_entered": "Maintenance mode exiting paused: Pet entered while in operation, anti-pinch sensors activated, device stopped working. Please check the device ASAP.", + "maintenance_exit_paused_cover": "Maintenance mode exiting paused: The top cover is not installed, operation paused", + "maintenance_exit_paused_system_error": "Maintenance mode exiting paused: System error, operation paused", + "maintenance_exit_paused_pet_approach": "Maintenance mode exiting paused: Your cat is approaching, operation paused", + "maintenance_exit_paused_pet_using": "Maintenance mode exiting paused: Your cat is using the device, operation paused", + "maintenance_exit_paused": "Maintenance mode exiting paused", + "idle": "Idle" + } + } + }, + "switch": { + "light": { + "name": "Light" + }, + "power": { + "name": "Power" + }, + "do_not_disturb": { + "name": "Do not disturb" + }, + "indicator_light": { + "name": "Indicator light" + }, + "child_lock": { + "name": "Child lock" + }, + "food_shortage_alarm": { + "name": "Food shortage alarm" + }, + "dispense_tone": { + "name": "Dispense tone" + }, + "voice_with_dispense": { + "name": "Voice with dispense" + }, + "surplus_control": { + "name": "Surplus control" + }, + "system_notification_sound": { + "name": "System notification sound" + }, + "auto_odor_removal": { + "name": "Auto odor removal" + }, + "auto_cleaning": { + "name": "Auto cleaning" + }, + "avoid_repeat_cleaning": { + "name": "Avoid repeat cleaning" + }, + "periodic_cleaning": { + "name": "Periodic cleaning" + }, + "periodic_odor_removal": { + "name": "Periodic odor removal" + }, + "kitten_mode": { + "name": "Kitten mode" + }, + "display": { + "name": "Display" + }, + "light_weight_cleaning_disabled": { + "name": "Light weight cleaning disabled" + }, + "cont_rotation": { + "name": "Continuous rotation" + }, + "deep_cleaning": { + "name": "Deep cleaning" + }, + "deep_deodor": { + "name": "Deep deodorization" + } + } } } diff --git a/custom_components/petkit/translations/hr.json b/custom_components/petkit/translations/hr.json new file mode 100644 index 0000000..64f3b8a --- /dev/null +++ b/custom_components/petkit/translations/hr.json @@ -0,0 +1,643 @@ +{ + "config": { + "abort": { + "already_configured": "PetKit račun je već konfiguriran", + "reauth_successful": "Ponovna provjera autentičnosti bila je uspješna" + }, + "error": { + "cannot_connect": "Povezivanje nije uspjelo", + "invalid_auth": "Nevažeća provjera autentičnosti. Jesu li vaše vjerodajnice točne?", + "no_devices": "Na vašem računu nisu pronađeni uređaji", + "server_busy": "PetKit serveri su zauzeti. Molimo pokušajte ponovo kasnije." + }, + "step": { + "user": { + "data": { + "email": "PetKit e-pošta računa", + "password": "PetKit lozinka računa" + }, + "title": "Ispunite svoje vjerodajnice za PetKit" + }, + "reauth_confirm": { + "data": { + "email": "PetKit e-pošta računa", + "password": "PetKit lozinka računa" + }, + "title": "Ponovo potvrdite autentičnost svojim PetKit vjerodajnicama" + } + } + }, + "entity": { + "binary_sensor": { + "water_level": { + "name": "Razina vode" + }, + "food_level": { + "name": "Razina hrane" + }, + "battery_installed": { + "name": "Baterija instalirana" + }, + "battery": { + "name": "Baterija" + }, + "wastebin": { + "name": "Kanta za smeće" + }, + "litter": { + "name": "Pijesak" + }, + "deodorizer": { + "name": "Dezodorans" + }, + "manually_paused": { + "name": "Ručno pauzirano" + }, + "pura_air_liquid": { + "name": "Pura Air tekućina" + } + }, + "button": { + "reset_filter": { + "name": "Resetiraj filter" + }, + "reset_desiccant": { + "name": "Resetiraj sredstvo za sušenje" + }, + "cancel_manual_feed": { + "name": "Otkažite ručno hranjenje" + }, + "call_pet": { + "name": "Nazovi ljubimca" + }, + "start_cleaning": { + "name": "Pokreni/Nastavi čišćenje" + }, + "pause_cleaning": { + "name": "Pauzirajte čišćenje" + }, + "odor_removal": { + "name": "Uklanjanje mirisa" + }, + "reset_deodorizer": { + "name": "Resetiraj dezodorans" + }, + "reset_pura_air_liquid": { + "name": "Resetiraj Pura Air tekućinu" + }, + "n50_reset": { + "name": "Resetiraj N50 eliminator mirisa" + }, + "light_on": { + "name": "Upali svjetlo" + }, + "start_maintenance": { + "name": "Započni održavanje" + }, + "exit_maintenance": { + "name": "Izlaz iz održavanja" + }, + "pause_exit_maintenance": { + "name": "Pauzirajte izlazak iz moda održavanja" + }, + "resume_exit_maintenance": { + "name": "Nastavak izlaska iz moda održavanja" + }, + "dump_litter": { + "name": "Baci mačji pijesak" + }, + "pause_dump_litter": { + "name": "Pauziraj odlaganje mačjeg pijeska" + }, + "resume_dump_litter": { + "name": "Nastavi s odlaganjem mačjeg pijeska" + } + }, + "number": { + "set_weight": { + "name": "Postavite težinu" + }, + "surplus": { + "name": "Višak" + }, + "volume": { + "name": "Volumen" + }, + "manual_feed": { + "name": "Ručno hranjenje" + }, + "cleaning_delay": { + "name": "Odgoda čišćenja" + } + }, + "select": { + "light_brightness": { + "name": "Svjetlina svjetla", + "state": { + "low": "Niska", + "medium": "Srednja", + "high": "Visoka" + } + }, + "mode": { + "name": "Način rada", + "state": { + "normal": "Normalan", + "smart": "Pametan" + } + }, + "manual_feed": { + "name": "Ručno hranjenje" + }, + "sound": { + "name": "Zvuk" + }, + "cleaning_interval": { + "name": "Interval čišćenja" + }, + "litter_type": { + "name": "Vrsta pijeska", + "state": { + "bentonite": "Bentonit", + "tofu": "Tofu", + "mixed": "Mješoviti" + } + } + }, + "sensor": { + "energy_usage": { + "name": "Potrošnja energije" + }, + "last_data_update": { + "name": "Zadnje ažuriranje podataka" + }, + "filter": { + "name": "Filter" + }, + "purified_water_today": { + "name": "Pročišćena voda danas" + }, + "feeder_status": { + "name": "Status", + "state": { + "offline": "Offline", + "normal": "Normalan", + "on_batteries": "Na Baterije" + } + }, + "desiccant_days_remaining": { + "name": "Sredstvo za sušenje dana preostalo" + }, + "battery_status": { + "name": "Status baterije", + "state": { + "normal": "Normalan", + "low": "Nizak" + } + }, + "dispensed": { + "name": "Izdano" + }, + "planned": { + "name": "Planirano" + }, + "planned_dispensed": { + "name": "Planirano izdano" + }, + "manually_dispensed": { + "name": "Ručno izdano" + }, + "times_dispensed": { + "name": "Puta izdano" + }, + "rssi": { + "name": "RSSI" + }, + "amount_eaten": { + "name": "Pojedena količina" + }, + "times_eaten": { + "name": "Puta jelo" + }, + "food_in_bowl": { + "name": "Hrana u posudi" + }, + "error": { + "name": "Greška", + "state": { + "no_error": "Nema Greške" + } + }, + "deodorizer_level": { + "name": "Razina dezodoransa" + }, + "litter_level": { + "name": "Razina pijeska" + }, + "litter_weight": { + "name": "Težina pijeska" + }, + "times_used": { + "name": "Puta Korištena" + }, + "average_use": { + "name": "Prosječna upotreba" + }, + "total_use": { + "name": "Ukupna upotreba" + }, + "last_used_by": { + "name": "Zadnje korištenje", + "state": { + "no_record_yet": "Još nema zapisa", + "unknown_pet": "Nepoznati kućni ljubimac" + } + }, + "last_event": { + "name": "Zadnji događaj", + "state": { + "no_events_yet": "Još nema događaja", + "event_type_unknown": "Vrsta događaja nepoznata", + "cleaning_completed": "Čišćenje završeno", + "dumping_over": "Izbacivanje gotovo", + "reset_over": "Ponovno postavljanje gotovo", + "spray_over": "Prskanje gotovo", + "pet_out": "Kućni ljubimac je izašao", + "auto_cleaning_completed": "Automatsko čišćenje dovršeno", + "periodic_cleaning_completed": "Periodično čišćenje završeno", + "manual_cleaning_completed": "Ručno čišćenje završeno", + "auto_cleaning_terminated": "Automatsko čišćenje prekinuto", + "periodic_cleaning_terminated": "Periodično čišćenje prekinuto", + "manual_cleaning_terminated": "Ručno čišćenje prekinuto", + "auto_cleaning_failed_full": "Automatsko čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, odmah je ispraznite", + "auto_cleaning_failed_hall_l": "Automatsko čišćenje nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "auto_cleaning_failed_hall_t": "Automatsko čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "scheduled_cleaning_failed_full": "Planirano čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, molimo da je odmah ispraznite", + "scheduled_cleaning_failed_hall_l": "Planirano čišćenje nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "scheduled_cleaning_failed_hall_t": "Planirano čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "manual_cleaning_failed_full": "Ručno čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, molimo da je odmah ispraznite", + "manual_cleaning_failed_hall_l": "Ručno čišćenje nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "manual_cleaning_failed_hall_t": "Ručno čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "auto_cleaning_canceled": "Automatsko čišćenje otkazano, uređaj radi", + "periodic_cleaning_canceled": "Periodično čišćenje otkazano, uređaj u radu", + "manual_cleaning_canceled": "Ručno čišćenje otkazano, uređaj u radu", + "auto_cleaning_canceled_kitten": "Način mačića je omogućen, automatsko čišćenje je poništeno", + "periodic_cleaning_canceled_kitten": "Način mačića je omogućen, povremeno čišćenje je otkazano", + "litter_empty_completed": "Pražnjenje pijeska završeno", + "litter_empty_terminated": "Pražnjenje pijeska prekinuto", + "litter_empty_failed_full": "Pražnjenje mačjeg pijeska nije uspjelo, kanta za sakupljanje otpada je puna, ispraznite odmah", + "litter_empty_failed_hall_l": "Pražnjenje mačjeg pijeska nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "litter_empty_failed_hall_t": "Pražnjenje mačjeg pijeska nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "reset_completed": "Resetiranje uređaja dovršeno", + "reset_terminated": "Resetiranje uređaja prekinuto", + "reset_failed_full": "Resetiranje uređaja nije uspjelo, kanta za sakupljanje otpada je puna, odmah je ispraznite", + "reset_failed_hall_l": "Resetiranje uređaja nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "reset_failed_hall_t": "Resetiranje uređaja nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "deodorant_finished": "Dezodorans je gotov", + "periodic_odor_completed": "Periodično uklanjanje mirisa završeno", + "manual_odor_completed": "Ručno uklanjanje mirisa dovršeno", + "deodorant_finished_liquid_lack": "Dezodorans je gotov, nema dovoljno tekućine za čišćenje, dopunite na vrijeme", + "periodic_odor_completed_liquid_lack": "Periodično uklanjanje neugodnih mirisa je završeno, nema dovoljno tekućine za pročišćavanje, dopunite na vrijeme", + "manual_odor_completed_liquid_lack": "Ručno uklanjanje neugodnih mirisa dovršeno, nema dovoljno tekućine za pročišćavanje, dopunite na vrijeme", + "auto_odor_failed": "Automatsko uklanjanje mirisa nije uspjelo, pogreška eliminatora mirisa", + "periodic_odor_failed": "Periodično uklanjanje mirisa nije uspjelo, kvar eliminatora mirisa", + "manual_odor_failed": "Ručno uklanjanje mirisa nije uspjelo, kvar eliminatora mirisa" + }, + "state_attributes": { + "sub_events": { + "state": { + "no_events_yet": "Još nema događaja", + "event_type_unknown": "Vrsta događaja nepoznata", + "cleaning_completed": "Čišćenje završeno", + "dumping_over": "Izbacivanje gotovo", + "reset_over": "Ponovno postavljanje gotovo", + "spray_over": "Prskanje gotovo", + "pet_out": "Kućni ljubimac je izašao", + "auto_cleaning_completed": "Automatsko čišćenje dovršeno", + "periodic_cleaning_completed": "Periodično čišćenje završeno", + "manual_cleaning_completed": "Ručno čišćenje završeno", + "auto_cleaning_terminated": "Automatsko čišćenje prekinuto", + "periodic_cleaning_terminated": "Periodično čišćenje prekinuto", + "manual_cleaning_terminated": "Ručno čišćenje prekinuto", + "auto_cleaning_failed_full": "Automatsko čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, odmah je ispraznite", + "auto_cleaning_failed_hall_l": "Automatsko čišćenje nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "auto_cleaning_failed_hall_t": "Automatsko čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "scheduled_cleaning_failed_full": "Planirano čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, molimo da je odmah ispraznite", + "scheduled_cleaning_failed_hall_l": "Planirano čišćenje nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "scheduled_cleaning_failed_hall_t": "Planirano čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "manual_cleaning_failed_full": "Ručno čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, molimo da je odmah ispraznite", + "manual_cleaning_failed_hall_l": "Ručno čišćenje nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "manual_cleaning_failed_hall_t": "Ručno čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "auto_cleaning_canceled": "Automatsko čišćenje otkazano, uređaj radi", + "periodic_cleaning_canceled": "Periodično čišćenje otkazano, uređaj u radu", + "manual_cleaning_canceled": "Ručno čišćenje otkazano, uređaj u radu", + "auto_cleaning_canceled_kitten": "Način mačića je omogućen, automatsko čišćenje je poništeno", + "periodic_cleaning_canceled_kitten": "Način mačića je omogućen, povremeno čišćenje je otkazano", + "litter_empty_completed": "Pražnjenje pijeska završeno", + "litter_empty_terminated": "Pražnjenje pijeska prekinuto", + "litter_empty_failed_full": "Pražnjenje mačjeg pijeska nije uspjelo, kanta za sakupljanje otpada je puna, ispraznite odmah", + "litter_empty_failed_hall_l": "Pražnjenje mačjeg pijeska nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "litter_empty_failed_hall_t": "Pražnjenje mačjeg pijeska nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "reset_completed": "Resetiranje uređaja dovršeno", + "reset_terminated": "Resetiranje uređaja prekinuto", + "reset_failed_full": "Resetiranje uređaja nije uspjelo, kanta za sakupljanje otpada je puna, odmah je ispraznite", + "reset_failed_hall_l": "Resetiranje uređaja nije uspjelo, cilindar nije pravilno zaključan na mjestu, molimo provjerite", + "reset_failed_hall_t": "Resetiranje uređaja nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "deodorant_finished": "Dezodorans je gotov", + "periodic_odor_completed": "Periodično uklanjanje mirisa završeno", + "manual_odor_completed": "Ručno uklanjanje mirisa dovršeno", + "deodorant_finished_liquid_lack": "Dezodorans je gotov, nema dovoljno tekućine za čišćenje, dopunite na vrijeme", + "periodic_odor_completed_liquid_lack": "Periodično uklanjanje neugodnih mirisa je završeno, nema dovoljno tekućine za pročišćavanje, dopunite na vrijeme", + "manual_odor_completed_liquid_lack": "Ručno uklanjanje neugodnih mirisa dovršeno, nema dovoljno tekućine za pročišćavanje, dopunite na vrijeme", + "auto_odor_failed": "Automatsko uklanjanje mirisa nije uspjelo, pogreška eliminatora mirisa", + "periodic_odor_failed": "Periodično uklanjanje mirisa nije uspjelo, kvar eliminatora mirisa", + "manual_odor_failed": "Ručno uklanjanje mirisa nije uspjelo, kvar eliminatora mirisa", + "no_sub_events": "Nema povezanih poddogađaja" + } + } + } + }, + "latest_weight": { + "name": "Najnovija težina" + }, + "last_use_duration": { + "name": "Trajanje zadnje upotrebe" + }, + "pura_air_battery": { + "name": "Pura Air baterija" + }, + "pura_air_liquid": { + "name": "Pura Air tekućina" + }, + "n50_odor_eliminator": { + "name": "N50 eliminator mirisa" + }, + "max_last_event": { + "name": "Last event", + "state": { + "no_events_yet": "Još nema događaja", + "event_type_unknown": "Vrsta događaja nepoznata", + "cleaning_completed": "Čišćenje završeno", + "dumping_over": "Izbacivanje gotovo", + "reset_over": "Ponovno postavljanje gotovo", + "spray_over": "Prskanje gotovo", + "light_over": "Svjetlo gotovo", + "pet_out": "Kućni ljubimac je izašao", + "auto_cleaning_completed": "Automatsko čišćenje dovršeno", + "periodic_cleaning_completed": "Periodično čišćenje završeno", + "manual_cleaning_completed": "Ručno čišćenje završeno", + "auto_cleaning_terminated": "Automatsko čišćenje prekinuto", + "periodic_cleaning_terminated": "Periodično čišćenje prekinuto", + "manual_cleaning_terminated": "Ručno čišćenje prekinuto", + "auto_cleaning_failed_full": "Automatsko čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, odmah je ispraznite", + "auto_cleaning_failed_hall_t": "Automatsko čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "auto_cleaning_failed_falldown": "Automatsko čišćenje nije uspjelo, kutija s pijeskom je srušena, molimo provjerite.", + "auto_cleaning_failed_other": "Automatsko čišćenje nije uspjelo, kvar uređaja, molimo provjerite.", + "scheduled_cleaning_failed_full": "Planirano čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, molimo da je odmah ispraznite", + "scheduled_cleaning_failed_hall_t": "Planirano čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "scheduled_cleaning_failed_falldown": "Zakazano čišćenje nije uspjelo, kutija za otpatke je srušena, molimo provjerite.", + "scheduled_cleaning_failed_other": "Zakazano čišćenje nije uspjelo, kvar uređaja, molimo provjerite.", + "manual_cleaning_failed_full": "Ručno čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, molimo da je odmah ispraznite", + "manual_cleaning_failed_hall_t": "Ručno čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "manual_cleaning_failed_falldown": "Ručno čišćenje nije uspjelo, kutija s pijeskom je srušena, molimo provjerite.", + "manual_cleaning_failed_other": "Ručno čišćenje nije uspjelo, kvar uređaja, molimo provjerite.", + "auto_cleaning_canceled": "Automatsko čišćenje otkazano, uređaj radi", + "periodic_cleaning_canceled": "Periodično čišćenje otkazano, uređaj u radu", + "manual_cleaning_canceled": "Ručno čišćenje otkazano, uređaj u radu", + "auto_cleaning_failed_maintenance": "Automatsko čišćenje nije uspjelo, uređaj je u načinu rada za održavanje", + "periodic_cleaning_failed_maintenance": "Povremeno čišćenje nije uspjelo, uređaj je u načinu rada za održavanje", + "auto_cleaning_canceled_kitten": "Način mačića je omogućen, automatsko čišćenje je poništeno", + "periodic_cleaning_canceled_kitten": "Način mačića je omogućen, povremeno čišćenje je otkazano", + "litter_empty_completed": "Pražnjenje pijeska završeno", + "litter_empty_terminated": "Pražnjenje pijeska prekinuto", + "litter_empty_failed_full": "Pražnjenje mačjeg pijeska nije uspjelo, kanta za sakupljanje otpada je puna, ispraznite odmah", + "litter_empty_failed_hall_t": "Pražnjenje mačjeg pijeska nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "litter_empty_failed_falldown": "Praznjenje pijeska za mačke nije uspjelo, kutija s pijeskom je srušena, molimo provjerite", + "litter_empty_failed_other": "Pražnjenje mačjeg pijeska nije uspjelo, kvar uređaja, molimo provjerite.", + "reset_completed": "Resetiranje uređaja dovršeno", + "reset_terminated": "Resetiranje uređaja prekinuto", + "reset_failed_full": "Resetiranje uređaja nije uspjelo, kanta za sakupljanje otpada je puna, odmah je ispraznite", + "reset_failed_hall_t": "Resetiranje uređaja nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "reset_failed_falldown": "Resetiranje uređaja nije uspjelo, kutija s pijeskom je srušena, molimo provjerite.", + "reset_failed_other": "Resetiranje uređaja nije uspjelo, kvar uređaja, molimo provjerite.", + "maintenance_mode": "Moda održavanje", + "deodorant_finished": "Dezodorans je gotov", + "periodic_odor_completed": "Periodično uklanjanje mirisa završeno", + "manual_odor_completed": "Ručno uklanjanje mirisa dovršeno", + "auto_odor_terminated": "Automatsko uklanjanje mirisa je prekinuto.", + "periodic_odor_terminated": "Periodično uklanjanje mirisa prekinuto.", + "manual_odor_terminated": "Ručno uklanjanje mirisa prekinuto.", + "auto_odor_failed": "Automatsko uklanjanje mirisa nije uspjelo, pogreška eliminatora mirisa", + "periodic_odor_failed": "Periodično uklanjanje mirisa nije uspjelo, kvar eliminatora mirisa", + "manual_odor_failed": "Ručno uklanjanje mirisa nije uspjelo, kvar eliminatora mirisa", + "auto_odor_canceled": "Automatsko uklanjanje mirisa je otkazano, uređaj radi.", + "periodic_odor_canceled": "Otkazano je periodično uklanjanje mirisa. Kutija za otpatke radi.", + "manual_odor_canceled": "Otkazano ručno uklanjanje neugodnih mirisa. Kutija za otpatke radi.", + "auto_odor_failed_device": "Automatsko uklanjanje mirisa nije uspjelo, nije spojen pametni sprej.", + "periodic_odor_failed_device": "Povremeno uklanjanje mirisa nije uspjelo. Uređaj za uklanjanje mirisa isključen.", + "manual_odor_failed_device": "Ručno uklanjanje mirisa nije uspjelo. Uređaj za uklanjanje mirisa isključen.", + "auto_odor_failed_batt": "Automatsko uklanjanje mirisa nije uspjelo, potvrdite da je baterija pametnog spreja dovoljna.", + "periodic_odor_failed_batt": "Povremeno uklanjanje mirisa nije uspjelo. Provjerite ima li uređaj za uklanjanje mirisa dovoljno baterije.", + "manual_odor_failed_batt": "Ručno uklanjanje mirisa nije uspjelo. Provjerite ima li uređaj za uklanjanje mirisa dovoljno baterije.", + "auto_odor_failed_low_batt": "Automatsko uklanjanje mirisa nije uspjelo, baterija je slaba.", + "periodic_odor_failed_low_batt": "Povremeno uklanjanje mirisa nije uspjelo. Niska baterija uređaja za uklanjanje mirisa.", + "manual_odor_failed_low_batt": "Ručno uklanjanje mirisa nije uspjelo. Niska baterija uređaja za uklanjanje mirisa.", + "cat_stopped_odor": "Vaša mačka koristi kutiju s pijeskom, dezodoriranje je otkazano", + "light_on": "Svjetlo je upaljeno", + "light_already_on": "Svjetlo je upaljeno. Nema potrebe za ponovnim uključivanjem.", + "light_malfunc": "Neuspjeh upaliti svjetlo. Kvar uređaja, molimo provjerite.", + "light_no_device": "Neuspjeh upaliti svjetlo. Prvo spoji uređaj za uklanjanje mirisa.", + "light_batt_cap": "Neuspjeh upaliti svjetlo. Provjerite kapacitet baterije uređaja za uklanjanje mirisa.", + "light_low_batt": "Neuspjeh upaliti svjetlo. Mali kapacitet baterije uređaja za uklanjanje mirisa." + }, + "state_attributes": { + "sub_events": { + "state": { + "no_events_yet": "Još nema događaja", + "event_type_unknown": "Vrsta događaja nepoznata", + "cleaning_completed": "Čišćenje završeno", + "dumping_over": "Izbacivanje gotovo", + "reset_over": "Ponovno postavljanje gotovo", + "spray_over": "Prskanje gotovo", + "light_over": "Svjetlo gotovo", + "pet_out": "Kućni ljubimac je izašao", + "auto_cleaning_completed": "Automatsko čišćenje dovršeno", + "periodic_cleaning_completed": "Periodično čišćenje završeno", + "manual_cleaning_completed": "Ručno čišćenje završeno", + "auto_cleaning_terminated": "Automatsko čišćenje prekinuto", + "periodic_cleaning_terminated": "Periodično čišćenje prekinuto", + "manual_cleaning_terminated": "Ručno čišćenje prekinuto", + "auto_cleaning_failed_full": "Automatsko čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, odmah je ispraznite", + "auto_cleaning_failed_hall_t": "Automatsko čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "auto_cleaning_failed_falldown": "Automatsko čišćenje nije uspjelo, kutija s pijeskom je srušena, molimo provjerite.", + "auto_cleaning_failed_other": "Automatsko čišćenje nije uspjelo, kvar uređaja, molimo provjerite.", + "scheduled_cleaning_failed_full": "Planirano čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, molimo da je odmah ispraznite", + "scheduled_cleaning_failed_hall_t": "Planirano čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "scheduled_cleaning_failed_falldown": "Zakazano čišćenje nije uspjelo, kutija za otpatke je srušena, molimo provjerite.", + "scheduled_cleaning_failed_other": "Zakazano čišćenje nije uspjelo, kvar uređaja, molimo provjerite.", + "manual_cleaning_failed_full": "Ručno čišćenje nije uspjelo, kanta za sakupljanje otpada je puna, molimo da je odmah ispraznite", + "manual_cleaning_failed_hall_t": "Ručno čišćenje nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "manual_cleaning_failed_falldown": "Ručno čišćenje nije uspjelo, kutija s pijeskom je srušena, molimo provjerite.", + "manual_cleaning_failed_other": "Ručno čišćenje nije uspjelo, kvar uređaja, molimo provjerite.", + "auto_cleaning_canceled": "Automatsko čišćenje otkazano, uređaj radi", + "periodic_cleaning_canceled": "Periodično čišćenje otkazano, uređaj u radu", + "manual_cleaning_canceled": "Ručno čišćenje otkazano, uređaj u radu", + "auto_cleaning_failed_maintenance": "Automatsko čišćenje nije uspjelo, uređaj je u načinu rada za održavanje", + "periodic_cleaning_failed_maintenance": "Povremeno čišćenje nije uspjelo, uređaj je u načinu rada za održavanje", + "auto_cleaning_canceled_kitten": "Način mačića je omogućen, automatsko čišćenje je poništeno", + "periodic_cleaning_canceled_kitten": "Način mačića je omogućen, povremeno čišćenje je otkazano", + "litter_empty_completed": "Pražnjenje pijeska završeno", + "litter_empty_terminated": "Pražnjenje pijeska prekinuto", + "litter_empty_failed_full": "Pražnjenje mačjeg pijeska nije uspjelo, kanta za sakupljanje otpada je puna, ispraznite odmah", + "litter_empty_failed_hall_t": "Pražnjenje mačjeg pijeska nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "litter_empty_failed_falldown": "Praznjenje pijeska za mačke nije uspjelo, kutija s pijeskom je srušena, molimo provjerite", + "litter_empty_failed_other": "Pražnjenje mačjeg pijeska nije uspjelo, kvar uređaja, molimo provjerite.", + "reset_completed": "Resetiranje uređaja dovršeno", + "reset_terminated": "Resetiranje uređaja prekinuto", + "reset_failed_full": "Resetiranje uređaja nije uspjelo, kanta za sakupljanje otpada je puna, odmah je ispraznite", + "reset_failed_hall_t": "Resetiranje uređaja nije uspjelo, gornji poklopac kutije s pijeskom nije pravilno postavljen, molimo provjerite", + "reset_failed_falldown": "Resetiranje uređaja nije uspjelo, kutija s pijeskom je srušena, molimo provjerite.", + "reset_failed_other": "Resetiranje uređaja nije uspjelo, kvar uređaja, molimo provjerite.", + "maintenance_mode": "Moda održavanje", + "deodorant_finished": "Dezodorans je gotov", + "periodic_odor_completed": "Periodično uklanjanje mirisa završeno", + "manual_odor_completed": "Ručno uklanjanje mirisa dovršeno", + "auto_odor_terminated": "Automatsko uklanjanje mirisa je prekinuto.", + "periodic_odor_terminated": "Periodično uklanjanje mirisa prekinuto.", + "manual_odor_terminated": "Ručno uklanjanje mirisa prekinuto.", + "auto_odor_failed": "Automatsko uklanjanje mirisa nije uspjelo, pogreška eliminatora mirisa", + "periodic_odor_failed": "Periodično uklanjanje mirisa nije uspjelo, kvar eliminatora mirisa", + "manual_odor_failed": "Ručno uklanjanje mirisa nije uspjelo, kvar eliminatora mirisa", + "auto_odor_canceled": "Automatsko uklanjanje mirisa je otkazano, uređaj radi.", + "periodic_odor_canceled": "Otkazano je periodično uklanjanje mirisa. Kutija za otpatke radi.", + "manual_odor_canceled": "Otkazano ručno uklanjanje neugodnih mirisa. Kutija za otpatke radi.", + "auto_odor_failed_device": "Automatsko uklanjanje mirisa nije uspjelo, nije spojen pametni sprej.", + "periodic_odor_failed_device": "Povremeno uklanjanje mirisa nije uspjelo. Uređaj za uklanjanje mirisa isključen.", + "manual_odor_failed_device": "Ručno uklanjanje mirisa nije uspjelo. Uređaj za uklanjanje mirisa isključen.", + "auto_odor_failed_batt": "Automatsko uklanjanje mirisa nije uspjelo, potvrdite da je baterija pametnog spreja dovoljna.", + "periodic_odor_failed_batt": "Povremeno uklanjanje mirisa nije uspjelo. Provjerite ima li uređaj za uklanjanje mirisa dovoljno baterije.", + "manual_odor_failed_batt": "Ručno uklanjanje mirisa nije uspjelo. Provjerite ima li uređaj za uklanjanje mirisa dovoljno baterije.", + "auto_odor_failed_low_batt": "Automatsko uklanjanje mirisa nije uspjelo, baterija je slaba.", + "periodic_odor_failed_low_batt": "Povremeno uklanjanje mirisa nije uspjelo. Niska baterija uređaja za uklanjanje mirisa.", + "manual_odor_failed_low_batt": "Ručno uklanjanje mirisa nije uspjelo. Niska baterija uređaja za uklanjanje mirisa.", + "cat_stopped_odor": "Vaša mačka koristi kutiju s pijeskom, dezodoriranje je otkazano", + "light_on": "Svjetlo je upaljeno", + "light_already_on": "Svjetlo je upaljeno. Nema potrebe za ponovnim uključivanjem.", + "light_malfunc": "Neuspjeh upaliti svjetlo. Kvar uređaja, molimo provjerite.", + "light_no_device": "Neuspjeh upaliti svjetlo. Prvo spoji uređaj za uklanjanje mirisa.", + "light_batt_cap": "Neuspjeh upaliti svjetlo. Provjerite kapacitet baterije uređaja za uklanjanje mirisa.", + "light_low_batt": "Neuspjeh upaliti svjetlo. Mali kapacitet baterije uređaja za uklanjanje mirisa.", + "no_sub_events": "Nema povezanih poddogađaja" + } + } + } + }, + "max_work_state": { + "name": "State", + "state": { + "cleaning_litter_box": "Trenutno čistim mačji pijesak", + "cleaning_litter_box_paused": "Čišćenje mačjeg pijeska pauzirano", + "cleaning_paused_pet_entered": "Čišćenje kutije s pijeskom pauzirano: ljubimac je ušao dok je radio, aktivirani su senzori protiv prignječenja, uređaj je prestao raditi. Provjerite uređaj što prije.", + "cleaning_paused_system_error": "Pauzirano čišćenje kutije za mačji pijesak: pogreška sustava, radnja pauzirana", + "cleaning_paused_pet_approach": "Pauzirano čišćenje kutije za pijesak za mačke: Vaša mačka se približava, operacija je pauzirana", + "cleaning_paused_pet_using": "Pauzirano čišćenje kutije za mačji pijesak: Vaša mačka koristi uređaj, postupak je pauziran", + "resetting_device": "Ponovno postavljanje uređaja", + "litter_box_paused": "Kutija za otpatke zastala", + "paused_pet_entered": "Kutija za mačji pijesak pauzirana: Kućni ljubimac je ušao dok je bio u radu, aktivirani su senzori protiv prignječenja, uređaj je prestao raditi. Provjerite uređaj što prije.", + "paused_system_error": "Kutija za mačji pijesak pauzirana: pogreška sustava, rad je pauziran", + "paused_pet_approach": "Kutija za mačji pijesak pauzirana: Vaša mačka se približava, operacija je pauzirana", + "paused_pet_using": "Kutija za mačji pijesak pauzirana: Vaša mačka koristi uređaj, rad je pauziran", + "dumping_litter": "Odlaganje mačjeg pijeska", + "dumping_litter_paused": "Odlaganje mačjeg pijeska zaustavljeno", + "dumping_paused_pet_entered": "Pauzirano odbacivanje mačjeg pijeska: ljubimac je ušao dok je radio, aktivirani su senzori protiv prignječenja, uređaj je prestao raditi. Provjerite uređaj što prije.", + "dumping_paused_system_error": "Odlaganje mačjeg pijeska pauzirano: Sistemska pogreška, radnja pauzirana", + "dumping_paused_pet_approach": "Odlaganje pijeska za mačke pauzirano: Vaša mačka se približava, radnja je pauzirana", + "dumping_paused_pet_using": "Pauzirano odbacivanje mačjeg pijeska: Vaša mačka koristi uređaj, postupak je pauziran", + "resetting": "Resetovanje", + "leveling": "Izravnavanje pijeska za mačke, pričekajte.", + "calibrating": "Kalibracija kutije za otpatke, molimo pričekajte.", + "maintenance_mode": "U načinu održavanja", + "maintenance_paused_pet_entered": "Način rada za održavanje pauziran: ljubimac je ušao tijekom rada, aktivirani su senzori protiv prignječenja, uređaj je prestao raditi. Provjerite uređaj što prije.", + "maintenance_paused_cover": "Način rada za održavanje pauziran: Gornji poklopac nije instaliran, rad je pauziran", + "maintenance_paused_system_error": "Način održavanja pauziran: Sistemska pogreška, rad pauziran", + "maintenance_paused_pet_approach": "Način održavanja pauziran: Vaša mačka se približava, rad je pauziran", + "maintenance_paused_pet_using": "Način održavanja pauziran: Vaša mačka koristi uređaj, rad je pauziran", + "maintenance_paused": "Način rada održavanja pauziran", + "exit_maintenance": "Izlaz iz načina održavanja", + "maintenance_exit_paused_pet_entered": "Izlazak iz načina održavanja pauziran: ljubimac je ušao tijekom rada, aktivirani su senzori protiv prignječenja, uređaj je prestao raditi. Provjerite uređaj što prije.", + "maintenance_exit_paused_cover": "Izlazak iz načina održavanja pauziran: Gornji poklopac nije postavljen, rad je pauziran", + "maintenance_exit_paused_system_error": "Izlazak iz načina održavanja pauziran: Sistemska pogreška, rad je pauziran", + "maintenance_exit_paused_pet_approach": "Izlazak iz načina održavanja pauziran: Vaša mačka se približava, rad je pauziran", + "maintenance_exit_paused_pet_using": "Izlazak iz načina održavanja pauziran: Vaša mačka koristi uređaj, rad je pauziran", + "maintenance_exit_paused": "Izlazak iz načina održavanja pauziran", + "idle": "Besposlen" + } + } + }, + "switch": { + "light": { + "name": "Svjetlo" + }, + "power": { + "name": "Struja" + }, + "do_not_disturb": { + "name": "Ne smetaj" + }, + "indicator_light": { + "name": "Svjetlosni indikator" + }, + "child_lock": { + "name": "Dječja brava" + }, + "food_shortage_alarm": { + "name": "Alarm za nestašicu hrane" + }, + "dispense_tone": { + "name": "Ton s dispenzom" + }, + "voice_with_dispense": { + "name": "Glas s dispenzom" + }, + "surplus_control": { + "name": "Kontrola viška" + }, + "system_notification_sound": { + "name": "Zvuk obavijesti sustava" + }, + "auto_odor_removal": { + "name": "Automatsko uklanjanje mirisa" + }, + "auto_cleaning": { + "name": "Automatsko čišćenje" + }, + "avoid_repeat_cleaning": { + "name": "Izbjegavajte ponavljanje čišćenja" + }, + "periodic_cleaning": { + "name": "Periodično čišćenje" + }, + "periodic_odor_removal": { + "name": "Povremeno uklanjanje mirisa" + }, + "kitten_mode": { + "name": "Način mačića" + }, + "display": { + "name": "Zaslon" + }, + "light_weight_cleaning_disabled": { + "name": "Onemogućeno lagano čišćenje" + }, + "cont_rotation": { + "name": "Kontinuirana rotacija" + }, + "deep_cleaning": { + "name": "Temeljito čišćenje" + }, + "deep_deodor": { + "name": "Temeljita dezodoracija" + } + } + } +} diff --git a/custom_components/petkit/util.py b/custom_components/petkit/util.py index 611e38e..3acf16a 100644 --- a/custom_components/petkit/util.py +++ b/custom_components/petkit/util.py @@ -5,7 +5,7 @@ import async_timeout from petkitaio import PetKitClient -from petkitaio.exceptions import AuthError +from petkitaio.exceptions import AuthError, ServerError from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -29,6 +29,9 @@ async def async_validate_api(hass: HomeAssistant, email: str, password: str) -> except AuthError as err: LOGGER.error(f'Could not authenticate on PetKit servers: {err}') raise AuthError(err) + except ServerError as err: + LOGGER.error(f'PetKit servers are busy.Please try again later.') + raise ServerError(err) except PETKIT_ERRORS as err: LOGGER.error(f'Failed to get information from PetKit servers: {err}') raise ConnectionError from err diff --git a/hacs.json b/hacs.json index 9348dc0..7fbbc6c 100644 --- a/hacs.json +++ b/hacs.json @@ -2,5 +2,5 @@ "name": "PetKit", "render_readme": true, "country": "US", - "homeassistant": "2023.1.0" + "homeassistant": "2023.4.0" }