├── README.md ├── custom_components └── mi_water_purifier │ ├── __init__.py │ ├── manifest.json │ └── sensor.py └── images ├── screenshot1.png ├── screenshot2.png └── screenshot3.png /README.md: -------------------------------------------------------------------------------- 1 | # homeassistant-mi-water-purifier 2 | XiaoMi Water Purifier component for Home Assistant. 3 | 4 | ![Screenshot1](https://raw.githubusercontent.com/bit3725/homeassistant-mi-water-purifier/master/images/screenshot1.png) 5 | ![Screenshot2](https://raw.githubusercontent.com/bit3725/homeassistant-mi-water-purifier/master/images/screenshot2.png) 6 | ![Screenshot3](https://raw.githubusercontent.com/bit3725/homeassistant-mi-water-purifier/master/images/screenshot3.png) 7 | 8 | ## Installation 9 | 1. Copy *custom_components/mi_water_purifier* to **.homeassistant/custom_components/**. 10 | 2. Get the IP of your sensor. 11 | 3. Follow [Retrieving the Access Token](https://home-assistant.io/components/vacuum.xiaomi_miio/#retrieving-the-access-token) guide to get the token of your sensor 12 | 13 | ## Configuration 14 | ```yaml 15 | sensor: 16 | - platform: mi_water_purifier 17 | host: YOUR_SENSOR_IP 18 | token: YOUR_SENSOR_TOKEN 19 | name: YOUT_SENSOR_NAME 20 | unique_id: YOUR_UNIQUE_ID 21 | ``` 22 | -------------------------------------------------------------------------------- /custom_components/mi_water_purifier/__init__.py: -------------------------------------------------------------------------------- 1 | """Support for Xiaomi water purifier.""" 2 | -------------------------------------------------------------------------------- /custom_components/mi_water_purifier/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "mi_water_purifier", 3 | "name": "XiaoMi Water Purifier", 4 | "version": "0.2.0", 5 | "documentation": "https://github.com/bit3725/homeassistant-mi-water-purifier", 6 | "requirements": [ 7 | "python-miio>=0.3.1" 8 | ], 9 | "dependencies": [], 10 | "codeowners": [ 11 | "@bit3725" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /custom_components/mi_water_purifier/sensor.py: -------------------------------------------------------------------------------- 1 | """Support for Xiaomi water purifier.""" 2 | import math 3 | import logging 4 | 5 | from homeassistant.const import (CONF_NAME, CONF_HOST, CONF_TOKEN, CONF_UNIQUE_ID) 6 | from homeassistant.helpers.entity import Entity 7 | from homeassistant.exceptions import PlatformNotReady 8 | from miio import Device, DeviceException 9 | from datetime import timedelta 10 | 11 | SCAN_INTERVAL = timedelta(seconds=5) 12 | _LOGGER = logging.getLogger(__name__) 13 | 14 | TAP_WATER_QUALITY = {'name': 'Tap water', 'key': 'ttds'} 15 | FILTERED_WATER_QUALITY = {'name': 'Filtered water', 'key': 'ftds'} 16 | PP_COTTON_FILTER_REMAINING = {'name': 'PP cotton filter', 'key': 'pfd', 'days_key': 'pfp'} 17 | FRONT_ACTIVE_CARBON_FILTER_REMAINING = {'name': 'Front active carbon filter', 'key': 'fcfd', 'days_key': 'fcfp'} 18 | RO_FILTER_REMAINING = {'name': 'RO filter', 'key': 'rfd', 'days_key': 'rfp'} 19 | REAR_ACTIVE_CARBON_FILTER_REMAINING = {'name': 'Rear active carbon filter', 'key': 'rcfd', 'days_key': 'rcfp'} 20 | 21 | 22 | def setup_platform(hass, config, add_devices, discovery_info=None): 23 | """Perform the setup for Xiaomi water purifier.""" 24 | 25 | host = config.get(CONF_HOST) 26 | name = config.get(CONF_NAME) 27 | token = config.get(CONF_TOKEN) 28 | unique_id = config.get(CONF_UNIQUE_ID) 29 | 30 | _LOGGER.info("Initializing Xiaomi water purifier with host %s (token %s...)", host, token[:5]) 31 | 32 | devices = [] 33 | try: 34 | device = Device(host, token) 35 | waterPurifier = XiaomiWaterPurifier(device, name, unique_id) 36 | devices.append(waterPurifier) 37 | devices.append(XiaomiWaterPurifierSensor(waterPurifier, TAP_WATER_QUALITY, unique_id)) 38 | devices.append(XiaomiWaterPurifierSensor(waterPurifier, FILTERED_WATER_QUALITY, unique_id)) 39 | devices.append(XiaomiWaterPurifierSensor(waterPurifier, PP_COTTON_FILTER_REMAINING, unique_id)) 40 | devices.append(XiaomiWaterPurifierSensor(waterPurifier, FRONT_ACTIVE_CARBON_FILTER_REMAINING, unique_id)) 41 | devices.append(XiaomiWaterPurifierSensor(waterPurifier, RO_FILTER_REMAINING, unique_id)) 42 | devices.append(XiaomiWaterPurifierSensor(waterPurifier, REAR_ACTIVE_CARBON_FILTER_REMAINING, unique_id)) 43 | except DeviceException: 44 | _LOGGER.exception('Fail to setup Xiaomi water purifier') 45 | raise PlatformNotReady 46 | 47 | add_devices(devices) 48 | 49 | 50 | class XiaomiWaterPurifierSensor(Entity): 51 | """Representation of a XiaomiWaterPurifierSensor.""" 52 | 53 | def __init__(self, waterPurifier, data_key, unique_id): 54 | """Initialize the XiaomiWaterPurifierSensor.""" 55 | self._state = None 56 | self._data = None 57 | self._waterPurifier = waterPurifier 58 | self._data_key = data_key 59 | self._attr_unique_id = unique_id + '_' + data_key['key'] 60 | self.parse_data() 61 | 62 | @property 63 | def name(self): 64 | """Return the name of the sensor.""" 65 | return self._data_key['name'] 66 | 67 | @property 68 | def icon(self): 69 | """Icon to use in the frontend, if any.""" 70 | if self._data_key['key'] is TAP_WATER_QUALITY['key'] or \ 71 | self._data_key['key'] is FILTERED_WATER_QUALITY['key']: 72 | return 'mdi:water' 73 | else: 74 | return 'mdi:filter-outline' 75 | 76 | @property 77 | def state(self): 78 | """Return the state of the device.""" 79 | return self._state 80 | 81 | @property 82 | def unit_of_measurement(self): 83 | """Return the unit of measurement of this entity, if any.""" 84 | if self._data_key['key'] is TAP_WATER_QUALITY['key'] or \ 85 | self._data_key['key'] is FILTERED_WATER_QUALITY['key']: 86 | return 'TDS' 87 | return '%' 88 | 89 | @property 90 | def device_state_attributes(self): 91 | """Return the state attributes of the last update.""" 92 | attrs = {} 93 | 94 | if self._data_key['key'] is PP_COTTON_FILTER_REMAINING['key'] or \ 95 | self._data_key['key'] is FRONT_ACTIVE_CARBON_FILTER_REMAINING['key'] or \ 96 | self._data_key['key'] is RO_FILTER_REMAINING['key'] or \ 97 | self._data_key['key'] is REAR_ACTIVE_CARBON_FILTER_REMAINING['key']: 98 | attrs[self._data_key['name']] = '{} days remaining'.format(self._data[self._data_key['days_key']]) 99 | 100 | return attrs 101 | 102 | def parse_data(self): 103 | if self._waterPurifier._data: 104 | self._data = self._waterPurifier._data 105 | self._state = self._data[self._data_key['key']] 106 | 107 | def update(self): 108 | """Get the latest data and updates the states.""" 109 | self.parse_data() 110 | 111 | 112 | class XiaomiWaterPurifier(Entity): 113 | """Representation of a XiaomiWaterPurifier.""" 114 | 115 | def __init__(self, device, name, unique_id): 116 | """Initialize the XiaomiWaterPurifier.""" 117 | self._state = None 118 | self._device = device 119 | self._name = name 120 | self._attr_unique_id = unique_id 121 | self.parse_data() 122 | 123 | @property 124 | def name(self): 125 | """Return the name of the device.""" 126 | return self._name 127 | 128 | @property 129 | def icon(self): 130 | """Icon to use in the frontend, if any.""" 131 | return 'mdi:water' 132 | 133 | @property 134 | def unit_of_measurement(self): 135 | """Return the unit of measurement of this entity, if any.""" 136 | return 'TDS' 137 | 138 | @property 139 | def state(self): 140 | """Return the state of the device.""" 141 | return self._state 142 | 143 | @property 144 | def hidden(self) -> bool: 145 | """Return True if the entity should be hidden from UIs.""" 146 | return True 147 | 148 | @property 149 | def device_state_attributes(self): 150 | """Return the state attributes of the last update.""" 151 | attrs = {} 152 | attrs[TAP_WATER_QUALITY['name']] = '{}TDS'.format(self._data[TAP_WATER_QUALITY['key']]) 153 | attrs[PP_COTTON_FILTER_REMAINING['name']] = '{}%'.format(self._data[PP_COTTON_FILTER_REMAINING['key']]) 154 | attrs[FRONT_ACTIVE_CARBON_FILTER_REMAINING['name']] = '{}%'.format( 155 | self._data[FRONT_ACTIVE_CARBON_FILTER_REMAINING['key']]) 156 | attrs[RO_FILTER_REMAINING['name']] = '{}%'.format(self._data[RO_FILTER_REMAINING['key']]) 157 | attrs[REAR_ACTIVE_CARBON_FILTER_REMAINING['name']] = '{}%'.format( 158 | self._data[REAR_ACTIVE_CARBON_FILTER_REMAINING['key']]) 159 | 160 | return attrs 161 | 162 | def parse_data(self): 163 | """Parse data.""" 164 | try: 165 | data = {} 166 | status = self._device.send('get_prop', []) 167 | data[TAP_WATER_QUALITY['key']] = status[0] 168 | data[FILTERED_WATER_QUALITY['key']] = status[1] 169 | pfd = int((status[11] - status[3]) / 24) 170 | data[PP_COTTON_FILTER_REMAINING['days_key']] = pfd 171 | data[PP_COTTON_FILTER_REMAINING['key']] = math.floor(pfd * 24 * 100 / status[11]) 172 | fcfd = int((status[13] - status[5]) / 24) 173 | data[FRONT_ACTIVE_CARBON_FILTER_REMAINING['days_key']] = fcfd 174 | data[FRONT_ACTIVE_CARBON_FILTER_REMAINING['key']] = math.floor(fcfd * 24 * 100 / status[13]) 175 | rfd = int((status[15] - status[7]) / 24) 176 | data[RO_FILTER_REMAINING['days_key']] = rfd 177 | data[RO_FILTER_REMAINING['key']] = math.floor(rfd * 24 * 100 / status[15]) 178 | rcfd = int((status[17] - status[9]) / 24) 179 | data[REAR_ACTIVE_CARBON_FILTER_REMAINING['days_key']] = rcfd 180 | data[REAR_ACTIVE_CARBON_FILTER_REMAINING['key']] = math.floor(rcfd * 24 * 100 / status[17]) 181 | 182 | self._data = data 183 | self._state = self._data[FILTERED_WATER_QUALITY['key']] 184 | except DeviceException: 185 | _LOGGER.exception('Fail to get_prop from Xiaomi water purifier') 186 | self._data = None 187 | self._state = None 188 | raise PlatformNotReady 189 | 190 | def update(self): 191 | """Get the latest data and updates the states.""" 192 | self.parse_data() 193 | -------------------------------------------------------------------------------- /images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit3725/homeassistant-mi-water-purifier/da42c59359f6e7d4d210ee3b5c051a2d91988a19/images/screenshot1.png -------------------------------------------------------------------------------- /images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit3725/homeassistant-mi-water-purifier/da42c59359f6e7d4d210ee3b5c051a2d91988a19/images/screenshot2.png -------------------------------------------------------------------------------- /images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit3725/homeassistant-mi-water-purifier/da42c59359f6e7d4d210ee3b5c051a2d91988a19/images/screenshot3.png --------------------------------------------------------------------------------