Add Support for Fujitsu wireless Air Conditioning control app - FGLair

@Marco_Simon do you have it running now?

1 Like

Today i got a chance to test the new file.

It needed a hard reboot,

But then …yes… i’ve got it up and running :smiley:

Thank you for your kind and patient help :wink: :smiley:

Still not 100% working.

I’ve got the climate component for the bedroom airco.
If i change the name in the fglair app after refresh it changes in hassio.

I can see the graph of the set temp and it changes if i turn on the airco by the fglair app.
And with a little bit of patience also by the component.

Fanmodes doesn’t seem to work, setting the fan to high doesn’t change the fan settings.
I don’t use this that often yet, so doesn’t bother me so far.

But i haven’t yet found a way to turn the airco on or off bij the component.
And this is a feature i really do like to have.

Please provide a screenshot of your component’s detail view. Attached is mine, action is set to off.

You seem to have a different model, where fan and swing modes are handled in an other way. Fixing this would need further API investigation from your side.

The delay is usual behavior, because the component regularly pulls the status.

When i put the climate component to cool.
Nothing hapens. In fglair app it states airco is on, but in fact the airco is off.

When i turn the airco on by the fglair app. Then set the airco component to heat, nothing changes but i also don’t get a error message

When i press swingmodus i get a error.
In the log it says:

Thu Oct 24 2019 13:30:53 GMT+0200 (Midden-Europese zomertijd)
'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 133, in handle_call_service
    connection.context(msg),
  File "/usr/src/homeassistant/homeassistant/core.py", line 1233, in async_call
    await asyncio.shield(self._execute_service(handler, service_call))
  File "/usr/src/homeassistant/homeassistant/core.py", line 1258, in _execute_service
    await handler.func(service_call)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 213, in handle_service
    self._platforms.values(), func, call, service_name, required_features
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 348, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 370, in _handle_service_platform_call
    await getattr(entity, func)(**data)
  File "/usr/src/homeassistant/homeassistant/components/climate/__init__.py", line 423, in async_set_swing_mode
    await self.hass.async_add_executor_job(self.set_swing_mode, swing_mode)
  File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/fujitsu_general_heatpump/climate.py", line 204, in set_swing_mode
    self._fujitsu_device.changeSwingMode(swing_mode)
  File "/config/deps/lib/python3.7/site-packages/pyfujitsu/splitAC.py", line 122, in changeSwingMode
    self.af_vertical_direction = 0
  File "/config/deps/lib/python3.7/site-packages/pyfujitsu/splitAC.py", line 335, in af_vertical_direction
    self._api._set_device_property(self.af_vertical_direction['key'],properties)
TypeError: 'NoneType' object is not subscriptable 

I see your AC has a lot more functionality than mine. Still, I ran into the same issue you described last week after an electricity outage. After a lot of trying, I was able to fix it today. I just disconnected my AC from the power for 1 minute and connected it again.
Afterwards everything worked again.

BTW, I added a lot of Debug logs. You can activate them by setting:

custom_components.fujitsu_general_heatpump: debug
custom_components.fujitsu_general_heatpump.climate: debug

In the logger.

Try to avoid playing around with swing modus for now. Just set a valid one with your remote and don’t touch it within Home Assistant.

All new versions stored locally on my Pi.

  • /config/custom_components/fujitsu_general_heatpump
  • /config/deps/lib/python3.7/site-packages/pyfujitsu

But still only error: “Platform error climate.fujitsu_general_heatpump - Integration ‘fujitsu_general_heatpump’ not found.”

It drives me nuts :wink:

All help appreciated…

Hi @vandenbogerd,

Have you followed the notes from @qwackers :
Add Support for Fujitsu wireless Air Conditioning control app - FGLair - #84 by qwackers

How is your file structure in your custom_components/fujitsu_general_heatpump folder

Have you enabled logging, described here:

Hi @philwilldo

Logger activated. Not going further than: “2019-11-11 09:34:42 ERROR (MainThread) [homeassistant.components.hassio] Platform error climate.fujitsu_general_heatpump - Integration ‘fujitsu_general_heatpump’ not found.”

Yes I followed the notes from @qwackers.

The filestructure is as followed:

I had to work on the electricity so put the power down for several minutes.
But still can’t turn the airco on with the climate unit.
I’ve activated the logger so see what turns out of that.

Hey All,

Anyone having issues with Home Assistant controlling their Fujitsu in the last week?

My hass install has been rock solid for months right up to a week ago coinciding with an update to the FGLAir app.

Logs throwing no errors, only evidence of not working is the A/C does not turn off or on. FGLAir app works perfectly…

How’s the experience for others?

Thanks Simon

1 Like

For me it isn’t working at all.

Not even status updates when i use the remote control

Hi, I just went from Hassbian to Hassio and needed to get this thing working again.
(HassOS 2.12, Hass.io 192)

Here is my working setup:

  1. Get the files:
    https://github.com/xerxes87/pyfujitsu_for_homeassistant/tree/master/custom_components/fujitsu_general_heatpump
  2. Put files in the directory as shown below:
    image
  3. Get these files too:
    https://github.com/xerxes87/pyfujitseu/releases/tag/0.9.3.0
  4. Put those files here:
    image
  5. I needed to do some modifications on the api.py to get it working for eu region. My complete modified file is shown below:
import logging
import requests
import time
import os
import json

HEADER_CONTENT_TYPE = "Content-Type"
HEADER_VALUE_CONTENT_TYPE = "application/json"
HEADER_AUTHORIZATION = "Authorization"

#version 0.9.2.7

_LOGGER = logging.getLogger(__name__)

def _api_headers(access_token=None):
    headers = {
        HEADER_CONTENT_TYPE: HEADER_VALUE_CONTENT_TYPE
    }

    if access_token:
        headers[HEADER_AUTHORIZATION] = 'auth_token ' + access_token

    return headers

class Api:
    def __init__(self,username,password,region='us',tokenpath='token.txt'):
        self.username = username
        self.password = password
        self.region = region
        
        if region == 'eu':
            _LOGGER.info('Region eu')
            self._SIGNIN_BODY = "{\r\n    \"user\": {\r\n        \"email\": \"%s\",\r\n        \"application\": {\r\n            \"app_id\": \"FGLair-eu-id\",\r\n            \"app_secret\": \"FGLair-eu-gpFbVBRoiJ8E3QWJ-QRULLL3j3U\"\r\n        },\r\n        \"password\": \"%s\"\r\n    }\r\n}"
            self._API_GET_ACCESS_TOKEN_URL = "https://user-field-eu.aylanetworks.com/users/sign_in.json"
            API_BASE_URL = "https://ads-field-eu.aylanetworks.com/apiv1/"
        elif region == 'cn':
            _LOGGER.info('Region cn')
            self._SIGNIN_BODY = "{\r\n    \"user\": {\r\n        \"email\": \"%s\",\r\n        \"application\": {\r\n            \"app_id\": \"FGLairField-cn-id\",\r\n            \"app_secret\": \"FGLairField-cn-zezg7Y60YpAvy3HPwxvWLnd4Oh4\"\r\n        },\r\n        \"password\": \"%s\"\r\n    }\r\n}"
            self._API_GET_ACCESS_TOKEN_URL = "https://user-field.ayla.com.cn/users/sign_in.json"
            API_BASE_URL = "https://ads-field.ayla.com.cn/apiv1/"
        else:
            _LOGGER.info('Region us')
            self._SIGNIN_BODY = "{\r\n    \"user\": {\r\n        \"email\": \"%s\",\r\n        \"application\": {\r\n            \"app_id\": \"CJIOSP-id\",\r\n            \"app_secret\": \"CJIOSP-Vb8MQL_lFiYQ7DKjN0eCFXznKZE\"\r\n        },\r\n        \"password\": \"%s\"\r\n    }\r\n}"
            self._API_GET_ACCESS_TOKEN_URL = "https://user-field.aylanetworks.com/users/sign_in.json"
            API_BASE_URL = "https://ads-field.aylanetworks.com/apiv1/"
        
        self._API_GET_PROPERTIES_URL = API_BASE_URL + "dsns/{DSN}/properties.json"
        self._API_SET_PROPERTIES_URL = API_BASE_URL + "properties/{property}/datapoints.json"
        self._API_GET_DEVICES_URL =  API_BASE_URL + "devices.json"

        self._ACCESS_TOKEN_FILE = tokenpath
    
    def _get_devices(self,access_token=None):
        if not self._check_token_validity(access_token):

          ## Token invalid requesting authentication
            access_token = self._authenticate()
        response = self._call_api("get",self._API_GET_DEVICES_URL,access_token=access_token)
        return response.json()

    def get_devices_dsn(self, access_token=None):
        devices = self._get_devices()
        devices_dsn = []
        for device in devices:
            devices_dsn.append(device['device']['dsn'])
        return devices_dsn
      
    def _get_device_properties(self,dsn):
        access_token = self._read_token()
        if not self._check_token_validity(access_token):
            access_token = self._authenticate()

        response = self._call_api("get",self._API_GET_PROPERTIES_URL.format(DSN=dsn),access_token=access_token)
        return response.json()

    def _set_device_property(self,propertyCode,value):
        access_token = self._read_token()
        if not self._check_token_validity(access_token):
            access_token = self._authenticate()

        response = self._call_api("post",self._API_SET_PROPERTIES_URL.format(property=propertyCode),propertyValue=value,access_token=access_token)

        return response
    
    def _get_device_property(self,propertyCode):
        access_token = self._read_token()
        if not self._check_token_validity(access_token):
            access_token = self._authenticate()

        response = self._call_api("get",self._API_SET_PROPERTIES_URL.format(property=propertyCode),access_token=access_token)
        ## Pay Attention the response is a HTTP request response object 
        #  and by doing .json you would get a List
        return response


    def _check_token_validity(self,access_token=None):
        if not access_token:
            return False        
        try:
            self._call_api("get",self._API_GET_DEVICES_URL,access_token=access_token)
        except:
            return False        
        return True


    def _authenticate(self):
        
        response = self._call_api("POST",
         self._API_GET_ACCESS_TOKEN_URL,
         json=self._SIGNIN_BODY % (self.username,self.password),
         headers= _api_headers())

        response.json()['time'] = int(time.time())

        access_token = response.json()['access_token']
    
        #refresh_token = response.json()['refresh_token']
        #expires_in = response.json()['expires_in']

        fd = os.open(self._ACCESS_TOKEN_FILE, os.O_WRONLY | os.O_CREAT, 0o777)
        f = open(fd, "w")

        #f = open(self._ACCESS_TOKEN_FILE, "w")
        f.write(response.text) 

        
        return access_token
    
    def _read_token(self,access_token_file=None):
        if not access_token_file:
            access_token_file = self._ACCESS_TOKEN_FILE
        if (os.path.exists(access_token_file) and os.stat(access_token_file).st_size != 0):
            f = open(access_token_file, "r")
            access_token_file_content = f.read()

            #now = int(time.time())

            access_token = json.loads(access_token_file_content)['access_token']
            #refresh_token = access_token_file_content.json()['refresh_token']
            #expires_in = access_token_file_content.json()['expires_in']
            #auth_time = int(access_token_file_content.json()['time'])
            return access_token
        else:
            return self._authenticate()


    
    def _call_api(self, method, url, access_token=None, **kwargs):
        payload = ''
                
        
        if "propertyValue" in kwargs:
            propertyValue = kwargs.get("propertyValue")
            kwargs["json"] = '{\"datapoint\": {\"value\": '+ str(propertyValue) +' } }'
        payload = kwargs.get("json")

        if "headers" not in kwargs:
            if access_token:
                kwargs["headers"] = _api_headers(access_token=access_token)
            else:
                kwargs["headers"] = _api_headers()
        
        if method.lower() == 'post':
            if not payload:
              raise Exception('Post method needs a request body!')

        
        response = requests.request(method, url, data=kwargs.get("json"),headers=kwargs.get("headers"))
        response.raise_for_status()
        return response

  1. My configuration.yaml setup:
    image

  2. token file location:
    image

  3. Restart Home Assistant

  4. Works like a dream!
    image

1 Like

I have same issue. what I have to insert in token.txt file?

Nothing, the token file will (should) be generated by the script.

I was eventually able to get this working. Even though I’m in the US region i also needed to make the same adjustments to the api.py file.

Hi,

I do have an issue sending a service to the AC.
I get

Failed to call service climate/set_hvac_mode. Service not found.

error massage.

Does anyone know, what is missing?
I am a newbie at Hass.io

The rest of the Fujitsu integration seems to work fine.

Thanks

Since upgrading to HA 0.108 the plug-in fails to load. Is anyone else seeing issues after upgrading.

Error while setting up fujitsu_general_heatpump platform for climate

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 178, in _async_setup_platform
    await asyncio.wait_for(asyncio.shield(task), SLOW_SETUP_MAX_WAIT)
  File "/usr/local/lib/python3.7/asyncio/tasks.py", line 442, in wait_for
    return fut.result()
  File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/fujitsu_general_heatpump/climate.py", line 77, in setup_platform
    if not fglairapi._authenticate():
  File "/config/deps/lib/python3.7/site-packages/pyfujitsu/api.py", line 111, in _authenticate
    headers= _api_headers())
  File "/config/deps/lib/python3.7/site-packages/pyfujitsu/api.py", line 17, in _api_headers
    HEADER_CONTENT_TYPE: HEADER_VALUE_CONTENT_TYPE
NameError: name 'HEADER_VALUE_CONTENT_TYPE' is not defined

I do have a similar/same issue:

Logger: homeassistant.helpers.service
Source: helpers/service.py:382 
First occurred: April 13, 2020, 10:59:59 PM (8 occurrences) 
Last logged: 10:51:54 AM

Unable to find referenced entities climate.sahan

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 178, in _async_setup_platform
    await asyncio.wait_for(asyncio.shield(task), SLOW_SETUP_MAX_WAIT)
  File "/usr/local/lib/python3.7/asyncio/tasks.py", line 442, in wait_for
    return fut.result()
  File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/fujitsu_general_heatpump/climate.py", line 77, in setup_platform
    if not fglairapi._authenticate():
  File "/config/deps/lib/python3.7/site-packages/pyfujitsu/api.py", line 120, in _authenticate
    fd = os.open(self._ACCESS_TOKEN_FILE, os.O_WRONLY | os.O_CREAT, 0o777)
TypeError: open: path should be string, bytes or os.PathLike, not NoneType
2020-04-13 22:44:03 WARNING (MainThread) [homeassistant.setup] Setup of default_config is taking over 10 seconds.
2020-04-13 22:46:11 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection.2940654448] must contain at least one of temperature, target_temp_high, target_temp_low.
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/voluptuous/schema_builder.py", line 272, in __call__
    return self._compiled([], data)
  File "/usr/local/lib/python3.7/site-packages/voluptuous/schema_builder.py", line 817, in validate_callable
    return schema(data)
  File "/usr/src/homeassistant/homeassistant/helpers/config_validation.py", line 115, in validate
    raise vol.Invalid("must contain at least one of {}.".format(", ".join(keys)))
voluptuous.error.Invalid: must contain at least one of temperature, target_temp_high, target_temp_low.

I posted the topic here as well: https://github.com/Mmodarre/pyfujitsu_for_homeassistant/issues/14

After a reinstall of the plugin it started working again. Made no changes so not sure why it was failing.