├── custom_components └── fujitsu_general_heatpump │ ├── __init__.py │ ├── manifest.json │ └── climate.py ├── ha_fujitsu.jpg ├── LICENSE └── README.md /custom_components/fujitsu_general_heatpump/__init__.py: -------------------------------------------------------------------------------- 1 | """ fujitsu_general_heatpump integration.""" -------------------------------------------------------------------------------- /ha_fujitsu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerxes87/pyfujitsu_for_homeassistant/HEAD/ha_fujitsu.jpg -------------------------------------------------------------------------------- /custom_components/fujitsu_general_heatpump/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "fujitsu_general_heatpump", 3 | "name": "Fujitsu general heatpump", 4 | "documentation": "https://github.com/Mmodarre/pyfujitsu_for_homeassistant/blob/master/README.md", 5 | "requirements": ["pyfujitseu"], 6 | "dependencies": [], 7 | "codeowners": [ 8 | "@Mmodarre", 9 | "@xerxes87" 10 | ], 11 | "version": "1.0.0" 12 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mmodarre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyfujitsu_for_homeassistant 2 | 3 | This is a platform to support Fujitsu General Airconditioners under Climate component of Home Assistant. The Python supporting library for accessing the FGLair API is located at: https://github.com/Mmodarre/pyfujitsu 4 | 5 | ### Sample UI: 6 | 7 | ![UI_SCREENSHOT](https://raw.githubusercontent.com/xerxes87/pyfujitsu_for_homeassistant/master/ha_fujitsu.jpg) 8 | 9 | ### Usage: 10 | 1. create this directory path `/config/custom_components/fujitsu_general_heatpump/` if it does not already exist. 11 | 12 | 13 | 2. Download the `climate.py` `manifest.json' and '__init__.py` from the repo and place it in the directory mentioned in previous step. 14 | 15 | So the end result would look like: 16 | `/config/custom_components/fujitsu_general_heatpump/climate.py` 17 | `/config/custom_components/fujitsu_general_heatpump/manifest.json` 18 | `/config/custom_components/fujitsu_general_heatpump/__init__.py` 19 | 20 | 3. add the below lines to your `configuration.yaml` file and replace it with your FGLair app username/password: 21 | ``` 22 | climate: 23 | - platform: fujitsu_general_heatpump 24 | username: 25 | password: 26 | region: [eu, cn, us] (optional, default: us) 27 | tokenpath: (optional, default: 'token.txt') 28 | ``` 29 | 30 | Full Example: 31 | ``` 32 | climate: 33 | - platform: fujitsu_general_heatpump 34 | username: !secret FGLAIR_USER 35 | password: !secret FGLAIR_PASS 36 | region: 'eu' 37 | tokenpath: 'token.txt' 38 | ``` 39 | 40 | 4. Restart Home Assistant in order for the new component to show and all of your A/Cs in your account should appear in HASS. 41 | 42 | ### Known issues and missing features: 43 | 44 | 45 | - [X] Logging needs to be implemented 46 | - [ ] The “powerful” functionality is implemented through aux_heat button in UI 47 | - [ ] There are some other functionalities in the A/C which currently is not implemented. 48 | - [ ] Possibility to add external temerature sensor 49 | -------------------------------------------------------------------------------- /custom_components/fujitsu_general_heatpump/climate.py: -------------------------------------------------------------------------------- 1 | """ 2 | Support for the Fujitsu General Split A/C Wifi platform AKA FGLair . 3 | """ 4 | 5 | import logging 6 | import voluptuous as vol 7 | from pyfujitseu.api import Api as fgapi 8 | from pyfujitseu.splitAC import splitAC 9 | 10 | 11 | from homeassistant.components.climate import ClimateEntity, PLATFORM_SCHEMA 12 | from homeassistant.components.climate.const import ( 13 | HVAC_MODE_OFF, 14 | HVAC_MODE_HEAT, 15 | HVAC_MODE_COOL, 16 | HVAC_MODE_AUTO, 17 | HVAC_MODE_DRY, 18 | HVAC_MODE_FAN_ONLY, 19 | SUPPORT_FAN_MODE, 20 | SUPPORT_SWING_MODE, 21 | SUPPORT_TARGET_TEMPERATURE, 22 | SUPPORT_AUX_HEAT, 23 | FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH, FAN_DIFFUSE, 24 | CURRENT_HVAC_HEAT, 25 | CURRENT_HVAC_IDLE) 26 | from homeassistant.const import ( 27 | ATTR_TEMPERATURE, CONF_USERNAME, CONF_PASSWORD, TEMP_CELSIUS) 28 | import homeassistant.helpers.config_validation as cv 29 | 30 | __version__ = '0.9.1' 31 | 32 | 33 | 34 | _LOGGER = logging.getLogger(__name__) 35 | 36 | #REQUIREMENTS = ['pyfujitseu==0.9.3.2'] 37 | 38 | # Values from web interface 39 | MIN_TEMP = 16 40 | MAX_TEMP = 30 41 | 42 | SUPPORT_FLAGS = SUPPORT_FAN_MODE | SUPPORT_SWING_MODE | SUPPORT_TARGET_TEMPERATURE 43 | 44 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ 45 | vol.Required(CONF_USERNAME): cv.string, 46 | vol.Required(CONF_PASSWORD): cv.string, 47 | vol.Optional('region'): cv.string, 48 | vol.Optional('tokenpath'): cv.string, 49 | 50 | }) 51 | 52 | 53 | HA_FAN_TO_FUJITSU = { 54 | FAN_AUTO: "Auto", 55 | FAN_LOW: "Low", 56 | FAN_MEDIUM: "Medium", 57 | FAN_HIGH: "High", 58 | FAN_DIFFUSE: "Quiet" 59 | } 60 | 61 | FUJITSU_FAN_TO_HA = { 62 | "Auto": FAN_AUTO, 63 | "Low": FAN_LOW, 64 | "Medium": FAN_MEDIUM, 65 | "High": FAN_HIGH, 66 | "Quiet": FAN_DIFFUSE 67 | } 68 | 69 | 70 | def setup_platform(hass, config, add_entities, discovery_info=None): 71 | """Setup the E-Thermostaat Platform.""" 72 | 73 | _LOGGER.debug("FujitsuClimate setup_platform called") 74 | _LOGGER.debug("FujitsuClimate setup_platform called") 75 | 76 | username = config.get(CONF_USERNAME) 77 | password = config.get(CONF_PASSWORD) 78 | region = config.get('region') 79 | tokenpath = config.get('tokenpath') 80 | _LOGGER.debug("FujitsuClimate config.get ") 81 | 82 | fglairapi = fgapi(username, password, region, tokenpath) 83 | if not fglairapi._authenticate(): 84 | _LOGGER.error("Unable to authenticate with Fujistsu General") 85 | return 86 | 87 | _LOGGER.debug("FujitsuClimate fglairapi._authenticate ") 88 | 89 | devices = fglairapi.get_devices_dsn() 90 | add_entities(FujitsuClimate(fglairapi, dsn) for dsn in devices) 91 | _LOGGER.debug("FujitsuClimate setup_platform fine") 92 | 93 | 94 | class FujitsuClimate(ClimateEntity): 95 | """Representation of a E-Thermostaat device.""" 96 | 97 | def __init__(self, api, dsn): 98 | """Initialize the thermostat.""" 99 | _LOGGER.debug("FujitsuClimate init called for dsn: %s", dsn) 100 | _LOGGER.debug("FujitsuClimate pyfujitseu.splitAC called") 101 | self._api = api 102 | self._dsn = dsn 103 | self._fujitsu_device = splitAC(self._dsn, self._api) 104 | _LOGGER.debug("FujitsuClimate _fujitsu_device setup.") 105 | self._name = self.name 106 | _LOGGER.debug("FujitsuClimate name set: %s", self._name) 107 | self._aux_heat = self.is_aux_heat_on 108 | self._target_temperature = self.target_temperature 109 | self._fan_mode = self.fan_mode 110 | self._hvac_mode = self.hvac_mode 111 | self._swing_mode = self.swing_mode 112 | 113 | self._fan_modes = [FUJITSU_FAN_TO_HA['Quiet'], FAN_LOW, FAN_MEDIUM, FAN_HIGH, FAN_AUTO] 114 | self._hvac_modes = [HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_AUTO, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_OFF] 115 | self._swing_modes = ['Horizontal' ,'Down', 'Unknown', 'Swing' ] 116 | self._on = self.is_on 117 | 118 | 119 | _LOGGER.debug("FujitsuClimate init fine.") 120 | 121 | @property 122 | def name(self): 123 | """Return the name of the thermostat.""" 124 | return self._fujitsu_device.device_name['value'] 125 | 126 | @property 127 | def is_aux_heat_on(self): 128 | """Reusing is for Powerfull mode.""" 129 | if not hasattr(self._fujitsu_device.powerful_mode, 'value'): 130 | return False 131 | elif self._fujitsu_device.powerful_mode['value'] == 1: 132 | return True 133 | else: 134 | return False 135 | 136 | @property 137 | def target_temperature(self): 138 | """Return the temperature we try to reach.""" 139 | return self._fujitsu_device.adjust_temperature_degree 140 | 141 | @property 142 | def target_temperature_step(self): 143 | """Return the supported step of target temperature.""" 144 | return 1 145 | 146 | 147 | @property 148 | def is_on(self): 149 | """Return true if on.""" 150 | if self._fujitsu_device.operation_mode['value'] != 0: 151 | return True 152 | else: 153 | return False 154 | 155 | @property 156 | def hvac_mode(self): 157 | """Return current operation ie. heat, cool, idle.""" 158 | _LOGGER.debug("FujitsuClimate hvac_mode: %s", self._fujitsu_device.operation_mode['value']) 159 | return self._fujitsu_device.operation_mode_desc 160 | 161 | @property 162 | def hvac_modes(self): 163 | """HVAC modes.""" 164 | return self._hvac_modes 165 | 166 | def set_hvac_mode(self, hvac_mode): 167 | """Set HVAC mode.""" 168 | _LOGGER.debug("FujitsuClimate set_hvac_mode called. self._hvac_mode: %s ; hvac_mode: %s", self._hvac_mode, hvac_mode) 169 | if(hvac_mode == HVAC_MODE_OFF): 170 | self._fujitsu_device.turnOff() 171 | elif(self._hvac_mode != hvac_mode): 172 | _LOGGER.debug("FujitsuClimate set_hvac_mode elif path called. ") 173 | self._fujitsu_device.changeOperationMode(hvac_mode) 174 | 175 | def set_temperature(self, **kwargs): 176 | """Set new target temperature.""" 177 | _LOGGER.debug("FujitsuClimate set_temperature: %s ; 2: %s", kwargs.get(ATTR_TEMPERATURE), kwargs.get(ATTR_TEMPERATURE)) 178 | self._fujitsu_device.changeTemperature(kwargs.get(ATTR_TEMPERATURE)) 179 | 180 | def update(self): 181 | """Retrieve latest state.""" 182 | self._fujitsu_device.refresh_properties() 183 | 184 | @property 185 | def fan_mode(self): 186 | """Return the fan setting.""" 187 | return FUJITSU_FAN_TO_HA[self._fujitsu_device.get_fan_speed_desc()] 188 | 189 | @property 190 | def fan_modes(self): 191 | """Return the list of available fan modes.""" 192 | return self._fan_modes 193 | 194 | def set_fan_mode(self, fan_mode): 195 | """Set fan mode.""" 196 | self._fujitsu_device.changeFanSpeed(HA_FAN_TO_FUJITSU[fan_mode]) 197 | 198 | @property 199 | def swing_mode(self): 200 | """Return the fan setting.""" 201 | return self._fujitsu_device.get_swing_mode_desc() 202 | 203 | @property 204 | def swing_modes(self): 205 | """List of available swing modes.""" 206 | return self._swing_modes 207 | 208 | def set_swing_mode(self, swing_mode): 209 | """Set new target temperature.""" 210 | self._fujitsu_device.changeSwingMode(swing_mode) 211 | 212 | ############old stufffff 213 | 214 | @property 215 | def unique_id(self) -> str: 216 | """Return the unique ID for this thermostat.""" 217 | return '_'.join([self._name, 'climate']) 218 | 219 | @property 220 | def should_poll(self): 221 | """Polling is required.""" 222 | return True 223 | 224 | @property 225 | def min_temp(self): 226 | """Return the minimum temperature.""" 227 | return MIN_TEMP 228 | 229 | @property 230 | def max_temp(self): 231 | """Return the maximum temperature.""" 232 | return MAX_TEMP 233 | 234 | @property 235 | def temperature_unit(self): 236 | """Return the unit of measurement.""" 237 | return TEMP_CELSIUS 238 | 239 | @property 240 | def supported_features(self): 241 | """Return the list of supported features.""" 242 | return SUPPORT_FLAGS --------------------------------------------------------------------------------