Hacking Electrolux Smart AC

Checkout this article (in Russian): Управляем конвектором и термостатом Electrolux из Home Assistant. Часть 1 / Хабр
This article has part 2 where author (Ailme) implemented custom component
Custom component repository is here: GitHub - Ailme/home_assistant_electrolux_remote: Integration Electrolux remote device in Home Assistant

1 Like

Good find!
Here’s the translated version of the Github repo: Google Translate

Did anyone try this component? My impression that it might not work for my Electrulux AC, a Well P7.

Hello! It’s work on Electrolux EACS/i-09HAT/N3 with ESPHome USB Stick. This is a completely working option with getting the state of the device, i.e. mode changes from the remote control are transmitted to the Home assistant.

There is also a solution for devices that work through the Home Comfort App. The author in the telegram channel writes that the specified model of the air conditioner will work, but I did not check this option due to the lack of the original ECH / WF-01 module

Good luck!

1 Like

Checked out the post but didn’t get any clever on it, anyone could clear it out for me?

Try to ask the questions you are interested in in the Telegram group. Preferably with translation into Russian. Telegram: Contact @midea_smart_russia

For those Aussies using Kelvinators I havn’t been able to add the Electrolux Remote Device Custom Integration yet into HA (emulating the Home Comfort app) and have added to an existing issue with the dev on GitHub.

So, this thread is becoming a dumping ground, in case smarter people then me see something I don’t or I get some ideas from peeps… Decompiled APK and found the file BLApiUrls.java under Sources > com > electrolux > aircondition > http > data > containing:

package com.electrolux.aircondition.http.data;

public class BLApiUrls {
    public static final String BAIDU_AUTH_INFO = "https://openapi.baidu.com/oauth/2.0/authorize";
    public static final String BAIDU_AUTH_INFO2 = "https://openapi.baidu.com/oauth/2.0/token";
    private static final String BASE_AC_URL = "https://cloudac.ibroadlink.com";
    public static String BASE_APP_MANAGE = "https://%sbizappmanage.ibroadlink.com";
    public static String BASE_FAMILY_URL = "https://%sbizihcv0.ibroadlink.com";
    private static final String BASE_TV_URL = "https://publictvir.ibroadlink.com";
    public static String BASE_VIRTUAL_DEV = "https://%sbizvdreg.ibroadlink.com";
    public static final String CLOUD_AC_BRAND_LIST = "https://cloudac.ibroadlink.com/cloudac/reqcloudname";
    public static String CLOUD_NEW_BASE = "https://%srccode.ibroadlink.com";
    public static final String CLOUD_TV_BRAND_LIST = "https://publictvir.ibroadlink.com/publictv/gettvbrand";
    public static String ELECTROLUX_URL = "https://%sthirdpartyservice.ibroadlink.com";
    public static String FAQ_URL = "https://kelvinatordownload.ibroadlink.com/soft/Kelvinator/help/";
    public static final String FEEDBACK = "https://feedback.ibroadlink.com/feedback/report";
    public static final String FW_VERSION = "https://fwversions.ibroadlink.com/getfwversion?devicetype=%1$s";
    public static final String GET_CITY_LIST = "https://publictvir.ibroadlink.com/publictv/getcity";
    public static final String GET_PROVIDER_LIST = "https://publictvir.ibroadlink.com/publictv/getprovider";
    public static final String GET_PROVINCES_LIST = "https://publictvir.ibroadlink.com/publictv/getprovince";
    public static String GOOGLEPLAY_URL = "https://play.google.com/store/apps/details?id=com.kelvinator.airconditioner";
    public static String GUIDE_URL = "https://kelvinatordownload.ibroadlink.com/soft/Kelvinator/help/#/answer/33";
    public static final String MS1_VERSION_URL = "https://upgrade.ibroadlink.com/sw/musicbox/stable/version.html";
    public static String PRIVACY_URL = "https://electrolux.ibroadlink.com/soft/privacy/#/";
    public static String PRIVACY_URL_AU = "https://www.kelvinator.com.au/other/privacy/";
    public static String PRIVACY_URL_NZ = "https://www.kelvinator.co.nz/other/privacy/";
    public static final String QT_FM_CATEGORIES = "http://api.qingting.fm/api/tongli/qtradiov4/categories?id=507&deviceid=%1$s";
    public static final String QT_FM_CHANNEL_LIST = "http://api.qingting.fm/api/tongli/qtradiov4/items";
    public static final String QT_FM_PROGRAM_LIST = "http://api.qingting.fm/api/tongli/qtradiov4/programs";
    public static final String QT_FM_RADIO_CATEGORY = "http://api.qingting.fm/api/tongli/qtradiov2/categories?id=100002&deviceid=%1$s";
    public static final String QT_FM_RADIO_STATION_LIST = "http://api.qingting.fm/api/tongli/qtradiov2/items";
    public static final String QUERY_BRAND_AC_LIST = "https://cloudac.ibroadlink.com/cloudac/oldversion/requrlbyname";
    public static final String QUERY_BRAND_TV_LIST = "https://publictvir.ibroadlink.com/publictv/geturldbybrand";
    public static final String QUERY_CHANNEL_HAS_NUM_LIST = "https://publictvir.ibroadlink.com/publictv/tvchannel/getlistchannel";
    public static final String QUERY_CITY_INFO = "https://publictvir.ibroadlink.com/publictv/query/areainfobyid";
    public static final String QUERY_COLUD_AC_RECOGNIZE_LIST = "https://cloudac.ibroadlink.com/cloudac/oldversion/recognize";
    public static final String QUERY_DEV_ELEC = "https://%srtasquery.ibroadlink.com/dataservice/v1/device/stats";
    public static final String QUERY_DEV_HISTORY = "https://%srtasquery.ibroadlink.com/dataservice/v1/device/status";
    public static final String QUERY_PROVIDER_CODE_LIST = "https://publictvir.ibroadlink.com/publictv/geturlbyarea";
    public static final String QUERY_PROVIDER_INFO = "https://publictvir.ibroadlink.com/publictv/query/providerinfobyid";
    public static final String QUERY_TV_CHANNEL_LIST = "https://publictvir.ibroadlink.com/publictv/tvchannel/getchannel";
    public static final String SPK_GET_ALL_RADIO_LIST = "https://ms1.ibroadlink.com/spkchannel?method=getall";
    public static final String SPK_GET_NET_RADIO_UPDATE_TIME = "https://ms1.ibroadlink.com/spkchannel?method=getupdatetime";
    public static final String SP_HOSTROY = "https://cn-clouddb.ibroadlink.com/spmini/history/status";
    public static String TERM_CONDITION_URL = "https://electrolux.ibroadlink.com/soft/terms/#/";
    public static String TERM_CONDITION_URL_AU = "https://www.kelvinator.com.au/other/terms-and-conditions/";
    public static String TERM_CONDITION_URL_NZ = "https://www.kelvinator.co.nz/other/terms-and-conditions/";
    public static String TEST_FAMILY_URL = "https://%sbizihcv0.ibroadlink.com";
    public static String TEST_MODELNUMBER_URL = "https://%sbizappmanage.ibroadlink.com";
    public static final String THRID_DEV_VALIDITY_CHECK_RUL = "https://3rddevrecognize.ibroadlink.com/ec/v1/thirdparty/productinfo/check";
    public static final String UPDATE_CHANNEL = "https://publictvir.ibroadlink.com/publictv/user/revisechannel";
    public static final String UPLOAD_REVISE_IRDA = "https://publictvir.ibroadlink.com/publictv/user/revisedata";
    public static final String WEIXIN_REQUEST_AUTHORIZE = "https://api.weixin.qq.com/device/authorize_device?access_token=";
    public static final String WEIXIN_REQUEST_QR = "https://api.weixin.qq.com/device/create_qrcode?access_token=";
    public static final String WEIXIN_REQUEST_TOKEN = "http://wechat.ibroadlink.com/control/key";
    public static final String XIMA_GET_CATEGORIES = "http://3rd.ximalaya.com/categories";
    public static final String XIMA_GET_CATEGORIES_ALBUMS = "http://3rd.ximalaya.com/categories/%1$s/hot_albums";
    public static final String XIMA_GET_CATEGORIES_TAGS = "http://3rd.ximalaya.com/categories/%1$s/tags";
    public static final String XIMA_SEARCH_ALBUMS = "http://3rd.ximalaya.com/search/albums";
    public static final String XIMA_SEARCH_VOICE = "http://3rd.ximalaya.com/search/tracks";

    public static void init(String str) {
        BASE_FAMILY_URL = String.format(BASE_FAMILY_URL, new Object[]{str});
        BASE_APP_MANAGE = String.format(BASE_APP_MANAGE, new Object[]{str});
        BASE_VIRTUAL_DEV = String.format(BASE_VIRTUAL_DEV, new Object[]{str});
        CLOUD_NEW_BASE = String.format(CLOUD_NEW_BASE, new Object[]{str});
        ELECTROLUX_URL = String.format(ELECTROLUX_URL, new Object[]{str});
        TEST_FAMILY_URL = String.format(BASE_FAMILY_URL, new Object[]{str});
        TEST_MODELNUMBER_URL = String.format(TEST_MODELNUMBER_URL, new Object[]{str});
    }

    public static class Electrolux {
        public static final String ADD_DEV_HW_INFO() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/dev/adddevhwinfo";
        }

        public static final String UPDATE_DEV_HW_INFO() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/dev/editdevhwinfo";
        }

        public static final String DELETE_DEV_HW_INFO() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/dev/deldevhwinfo";
        }

        public static final String GET_DEV_HW_INFO() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/dev/getdevhwinfo";
        }

        public static final String GET_MODELNUMBER_LIST() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/dev/getmodelnumberlist";
        }

        public static final String GET_DEV_PID() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/dev/getdevtype";
        }

        public static final String GET_DEV_PID_QRCODE() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/resource/product/qrcode";
        }

        public static final String GET_DEV_PID_QRCODE_NEW() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/language/product/qrcode";
        }

        public static final String ADD_TIMETASK() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/timetask/manage/add";
        }

        public static final String UPDATE_TIMETASK() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/timetask/manage/update";
        }

        public static final String DELETE_TIMETASK() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/timetask/manage/del";
        }

        public static final String QUERY_TIMETASK() {
            return BLApiUrls.ELECTROLUX_URL + "/thirdparty/v1/timetask/query";
        }

        public static final String QRCODE_GENERATE() {
            return BLApiUrls.TEST_FAMILY_URL + "/ec4/v1/dev/qrcode/generate";
        }

        public static final String QRCODE_SCAN() {
            return BLApiUrls.TEST_FAMILY_URL + "/ec4/v1/dev/qrcode/scan";
        }

        public static final String DEVICE_DELETE() {
            return BLApiUrls.TEST_FAMILY_URL + "/ec4/v1/dev/share/delete";
        }

        public static final String QUERY_SHAREFAMILY_MEMBER() {
            return BLApiUrls.TEST_FAMILY_URL + "/ec4/v1/dev/sharefamily/query";
        }

        public static final String DELETE_SHAREFAMILY_MEMBER() {
            return BLApiUrls.TEST_FAMILY_URL + "/ec4/v1/dev/sharefamily/delete";
        }

        public static final String GET_PRICATEGORY_LIST() {
            return BLApiUrls.TEST_MODELNUMBER_URL + "/ec4/v1/system/language/category/list";
        }

        public static final String GET_CATEGORY_LIST() {
            return BLApiUrls.TEST_MODELNUMBER_URL + "/ec4/v1/system/language/product/list";
        }

        public static final String GET_QRCODE_LIST() {
            return BLApiUrls.TEST_MODELNUMBER_URL + "/ec4/v1/system/getproductlist";
        }
    }

    public static class AppManager {
        public static final String PRODUCT_ICON() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/configfile";
        }

        public static final String ADD_PRODUCT_DIRECTORY() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/language/category/list";
        }

        public static final String ADD_PRODUCT_LIST() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/language/product/list";
        }

        public static final String ADD_PRODUCT_DETAIL() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/language/product/info";
        }

        public static final String ADD_QR_PRODUCT_DETAIL() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/language/product/qrcode";
        }

        public static final String GET_SP_APPLIANCE_LIST() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/getsmartplugapp";
        }

        public static final String QUERY_DEFAULT_ROOM() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/defineroom";
        }

        public static final String APPLY_VIRTUAL_DEV() {
            return BLApiUrls.BASE_VIRTUAL_DEV + "/ec/v2/thirdparty/dev/didapply";
        }

        public static final String DEVICE_AUTH() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/auth/add";
        }

        public static final String DEVICE_AUTH_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/auth/query";
        }

        public static final String LOCATION_CONFIG_URL() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/config/getconfig?mtag=locationconfig";
        }

        public static final String GET_SCENE_DETAIL() {
            return BLApiUrls.BASE_APP_MANAGE + "/ec4/v1/system/scenedetail";
        }
    }

    public static class Family {
        public static final String RM_TC() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/model/other/tc_home_big_off.png";
        }

        public static final String RM_AC() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/icon_module_rm_ac.png";
        }

        public static final String RM_TV() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/icon_module_rm_tv.png";
        }

        public static final String RM_STB() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/icon_module_rm_stb.png";
        }

        public static final String RM_CHANNEL() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/icon_module_rm_stb_channel.png";
        }

        public static final String RM_CTRL_CENTRE() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/icon_module_total_control.png";
        }

        public static final String RM_STATISTICS() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/icon_module_statistics.png";
        }

        public static final String RM_LAMP() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/lamp_home_big_off.png";
        }

        public static final String RM_CURTAIN() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/curtain_on.png";
        }

        public static final String RM_COMMON() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/moduleicon/commonpanel.png";
        }

        public static final String APP_VERSION_URL() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/app/update";
        }

        public static final String APP_HELP_URL() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4appsysinfo/helppage/help.html";
        }

        public static final String FAMILY_REQUEST_TIMESTAMP() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/common/api";
        }

        public static final String GET_DEVICE_FAMILY() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/dev/getfamily";
        }

        public static final String GET_RM_BTN_ICON_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/system/defineircodeicon";
        }

        public static final String CREATE_FAMILY() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/family/add";
        }

        public static final String EDIT_FAMILY_INFO() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/family/modifyinfo";
        }

        public static final String EDIT_FAMILY_ICON() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/family/modifyicon";
        }

        public static final String EDIT_ROOM_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/room/manage";
        }

        public static final String REQUEST_FAMILY_QRCODE() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/member/invited/reqqrcode";
        }

        public static final String REQUEST_QRCODE_FAMILY_INFO() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/member/invited/scanqrcode";
        }

        public static final String FAMILY_JOIN_BY_QR() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/member/invited/joinfamily";
        }

        public static final String JOIN_PUBLIC_FAMILY() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/member/joinpublicfamily";
        }

        public static final String FAMILY_NUM_QUERY() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/member/getfamilymember";
        }

        public static final String FAMILY_NUM_DELETE() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/member/delfamilymember";
        }

        public static final String GET_CONFIG_DEVICE_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/dev/getconfigdev";
        }

        public static final String GET_FAMILY_VERSION_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/user/getfamilyid";
        }

        public static final String GET_FAMILY_VERSION() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/family/getversion";
        }

        public static final String GET_FAMILY_LIST_DETAL_INFO() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/family/getallinfo";
        }

        public static final String GET_CATEGORY_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/system/getchilddir";
        }

        public static final String ADD_MODULE() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/add";
        }

        public static final String MODIFY_MODULE_ORDER() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/modifyorder";
        }

        public static final String ADD_MODULE_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/addlist";
        }

        public static final String EDIT_MODULE_FLAG() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/modifyflag";
        }

        public static final String EDIT_MODULE_INFO_AND_ROOM() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/modifyandmovemodule";
        }

        public static final String EDIT_MODULE() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/modify";
        }

        public static final String EDIT_MODULE_NEW() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/modifybasicinfo";
        }

        public static final String DELETE_MODULE() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/del";
        }

        public static final String DELETE_DEV() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/dev/deldev";
        }

        public static final String MODFIY_MODULE_ROOM() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/movemodule";
        }

        public static final String SET_PEAK_INFO() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/electricinfo/config";
        }

        public static final String QUERY_PEAK_INFO() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/electricinfo/query";
        }

        public static final String DELETE_FAMILY() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/family/del";
        }

        public static final String QUIT_FAMILY() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/member/quitfamily";
        }

        public static final String DEV_ROOM_EDIT() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/dev/movedev";
        }

        public static final String ADD_PIC() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/system/addpic";
        }

        public static final String MODULE_NAME_EDIT() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/modifyname";
        }

        public static final String MODULE_RELATION_EDIT() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/module/modifyrelation";
        }

        public static final String SET_USER_PRIVATE_DAT() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/family/upsertprivatedata";
        }

        public static final String GET_USER_PRIVATE_DAT() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/family/getprivatedata";
        }

        public static final String LINKAGE_ADD() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/linkage/add";
        }

        public static final String LINKAGE_DELETE() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/linkage/delete";
        }

        public static final String LINKAGE_QUERY() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/linkage/query";
        }

        public static final String LINKAGE_UPDATE() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/linkage/update";
        }

        public static final String DELETE_FAMILY_DEVICE_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/dev/dellist";
        }

        public static final String AUTH_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/authmanager/auth/list";
        }

        public static final String GET_DEV_LIST_BY_AUTH() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/authmanager/auth/devlist/query";
        }

        public static final String UPDATE_DEV_LIST_BY_AUTH() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/authmanager/auth/devlist/update";
        }

        public static final String GET_AUTHORIZED_DEV_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/authmanager/dev/list";
        }

        public static final String GET_DEV_AUTH_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/authmanager/dev/authlist/query";
        }

        public static final String UPDATE_DEV_AUTH_LIST() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/authmanager/dev/authlist/update";
        }

        public static final String GET_SCENE_TYPE_ADD() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/moduleclassify/add";
        }

        public static final String GET_SCENE_TYPE_DEL() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/moduleclassify/del";
        }

        public static final String GET_SCENE_TYPE_MOD() {
            return BLApiUrls.BASE_FAMILY_URL + "/ec4/v1/moduleclassify/modify";
        }
    }

    public static class IrdaAPI {
        public static String CLOUD_BRAND_LIST() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/app/getbrand";
        }

        public static String CLOUD_BRAND_CLASS_LIST() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/app/geturlbybrandversion?mtag=app";
        }

        public static String CLOUD_AC_RECOGNIZE_LIST() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/cloudac/recognizeirdata?mtag=app";
        }

        public static String CLOUD_PROVINCES_LIST() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/app/getsubarea";
        }

        public static String CLOUD_PROVIDERS_LIST() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/stb/getprovider";
        }

        public static String CLOUD_PROVINCES_CODE_LIST() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/stb/geturlbyarea";
        }

        public static String CLOUD_AREA_BY_ID() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/app/getareainfobyid";
        }

        public static String CLOUD_PROVIDER_BY_ID() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/stb/getproviderinfobyid";
        }

        public static String CLOUD_CHANNEL_LIST_BY_ID() {
            return BLApiUrls.CLOUD_NEW_BASE + "/publicircode/v2/stb/getchannel";
        }
    }
}

The % in the URL is the Licence ID captured by Mitmproxy… API call with token results to:

{"error":-2002,"status":-2002,"msg":"数据错误"}

In other words:

Screen Shot 2021-11-29 at 3.58.57 pm

The data is in the post messages is encrypted with AES (its not application/x-java-serialized-object), so writing a client should not be impossible.

I’d say so yeah, it is hitting the limit of my skillsets though :sweat:. I actually, perhaps stupidly, ordered two more of these units on Black Friday sales (was a good deal, saved about $200) for my remaining two bedrooms so keen to figure this out, just not sure on what to look at next.

Also worth pointing, that because it has a Broadlink Wifi Chip, I tried this code from Liaan - GitHub - liaan/broadlink_ac_mqtt: Broadlink Air Conditioners to mqtt. But unfortunately, couldn’t detect anything.

Its a strange sdk, sha1 implementation is:

string -> sha256 -> sha1

And they are doing a lot of string concats before they encrypt with the “secret” key(s).

So its probably necessary to mimic the behavior in python.

I detects my AC, its not the right device type, but it can see it in the discovery

MAC etc. has been masked in the log:

Listing on /aircon/+/+/set for messages
publishing on topic "/aircon/LWT", data "online"
Recieved response! devtype: 20379 mac: bytearray(b'\xff\xff\xff\xfff') name: ELECTROLUX_OEM host: ('x.x.x.x', 80) cloud: False
device: <broadlink_ac_mqtt.classes.broadlink.ac_db.device object at 0xb5f7ef70> device.devtype: 0x4f9b
Returning devices: {}
something went wrong, no devices found
Stopping

They only support dev_type: 0x4E2A, my AC:s device type is: 0x4f9b


    def discover(self):

        # Go discovery
        discovered_devices = broadlink.discover(
            timeout=5, bind_to_ip=self.config["bind_to_ip"]
        )
        devices = {}

        if discovered_devices == None:
            error_msg = "No Devices Found, make sure you on the same network segment"
            logger.debug(error_msg)

            # print "nothing found"
            sys.exit()

        # Make sure correct device id
        for device in discovered_devices:
            logging.debug(f"device: {device} device.devtype: {hex(device.devtype)}")
            if device.devtype == 0x4E2A:
                devices[device.status["macaddress"]] = device

        logging.debug(f"Returning devices: {devices}")

        return devices

So its probably easiest to fork it and continue to “hack” on it.

Hmm, that’s interesting, mine literally comes up with nothing in MQTT other than LWT=online

The logs of the script (in Docker) are:

Starting Monitor...
something went wrong, no devices found
Stopping
Starting Monitor...
something went wrong, no devices found
Stopping

What model aircon do you have?

I forked the project and added logging GitHub - hellqvio86/broadlink_ac_mqtt: Broadlink Air Conditioners to mqtt

If I do something I will probably discard the project and build a library that is easier to work with, but that depends on how much time I have, have a new born etc. :slight_smile:

But the AC is a WP71-265WT

Ah yeah, looks like it uses the Electrolux Home Comfort app, mine uses the Kelvinator Home Comfort app (Electrolux owning Kelvinator) which make me think ours are pretty similar.

I’ve got this one - https://www.thegoodguys.com.au/kelvinator/heating-and-cooling/air-conditioners

Ideally it would be great if it was API connected but MQTT communication would suffice. I’ll keep trying but it does look like your skillsets are much greater than mine :smiley:

Oh kids, enjoy regular naps when you can (that’s when I got all my tinkering done). Once they hit toddler, all hell breaks loose and all your projects go to the corner and die. Congratulations anyway, I’ve got my 2nd one arriving early March so my life is mostly finishing off as many renovations as I can before that.

Yep, the difference is probably only the vendor id hopefully, IV and the static AES keys might differ, but probably not I hope.

I would love to be able to talk directly from Home Assistant to the AC using the local api, so if I go for it, its probably the first thing I try :slight_smile:

The project you linked to is using broadcast messages, so home assistant needs to be on the same vlan/network, which my is, so having an integration for it would be neat :slight_smile:

1 Like

I think it is an easy try just to change in the code the 4E2A to 4F9B and see what it does.

The error message after comes from this part of the code:

devices_array = self.make_devices_array_from_devices(devices)
        if devices_array == {}:
            print("something went wrong, no devices found")
            sys.exit()

And it is due to the fact that devices is empty because the if statement fulfills it with the MAC address only if the device type is 4E2A.

Update:

So, assuming that the 4F9B works the same way as the 4E2A, you need to update a few things in the ac_db.py as well.

Just search for type and where you see reference to 4E2A just update it to 4F9B, especially at the beginning of the file and in the send_packet classes. (or whatever python calls it, I have never learned python as language…) If you will do a search for 4E, you will see where.

And keep the logs, as the encryption seems to be AES from the code you will be able to decode it manually if something is not working.

Edit:

@TJohnson93 try @hellqvio’s fork, that will first of all give you your device type, then you can try together if it works with your device type. (I’ve seen your post on the ST Community Forum, and you are in a better place here, unless ST supports Electrolux natively, but I haven’t heard about that.)

So when I ran this script prior I used a docker container, trying to run on native (virtual env) OS now from your fork (happens with the main repo as well) I can yaml module error, however pyyaml is installed, same version under both pip and pip3:

Traceback (most recent call last):
  File "./monitor.py", line 3, in <module>
    import yaml
ImportError: No module named yaml

pyyaml versions:

(.venv) REDACTED@MacBook-Air docker-python % pip install pyyaml
Requirement already satisfied: pyyaml in ./.venv/lib/python3.10/site-packages (6.0)
(.venv) REDACTED@MacBook-Air docker-python % pip3 install pyyaml
Requirement already satisfied: pyyaml in ./.venv/lib/python3.10/site-packages (6.0)

have even reinstalled all from host os and within the venv, ever

(.venv) REDACTED@MacBook-Air docker-python % which python
/Users/REDACTED/Documents/docker-python/.venv/bin/python
(.venv) REDACTED@MacBook-Air docker-python % pip list
Package    Version
---------- -------
paho-mqtt  1.6.1
pip        21.3.1
pycrypto   2.6.1
PyYAML     6.0
setuptools 59.5.0

Damn thing is odd, damn Mac’s!!! everything is installed and pointing to the virtualnev

(.venv) REDACTED@MacBook-Air docker-python % pip freeze
paho-mqtt==1.6.1
pycrypto==2.6.1
PyYAML==6.0
(.venv) REDACTED@MacBook-Air docker-python % pip -V
pip 21.3.1 from /Users/toddjohnson/Documents/docker-python/.venv/lib/python3.10/site-packages/pip (python 3.10)
(.venv) REDACTED@MacBook-Air docker-python % python -V
Python 3.10.1
(.venv) REDACTED@MacBook-Air docker-python % which python
/Users/REDACTED/Documents/docker-python/.venv/bin/python
(.venv) REDACTED@MacBook-Air docker-python % which pip
/Users/REDACTED/Documents/docker-python/.venv/bin/pip

EDIT - I got it to run after running as python3 monitor.py and then removing the import parser line from ac_db.py… Will post result in new comment.

forked and ran @hellqvio fork. got:

(.venv) REDACTED@MacBook-Air docker-python % python3 monitor.py -S
/Users/REDACTED/Documents/docker-python/monitor.py v1.1.0 is starting up
Loglevel set to DEBUG
Binding to ip 192.168.1.226
Recieved response! devtype: 25755 mac: bytearray(b'$\xf\xf\xf\t!') name: 智能遥控 host: ('x.x.x.x', 80) cloud: False
device: <broadlink_ac_mqtt.classes.broadlink.ac_db.device object at 0x10399a6b0> device.devtype: 0x649b
Returning devices: {}
No devices found, make sure you are on same network broadcast segment as device/s

EDIT: updating the device type id results in:

Debugging Enabled
Authenticating
Traceback (most recent call last):
  File "/Users/REDACTED/Documents/docker-python/monitor.py", line 390, in <module>
    start()
  File "/Users/REDACTED/Documents/docker-python/monitor.py", line 329, in start
    discover_and_dump_for_config(config)
  File "/Users/REDACTED/Documents/docker-python/monitor.py", line 34, in discover_and_dump_for_config
    devices = AC.discover()
  File "/Users/REDACTED/Documents/docker-python/broadlink_ac_mqtt/AcToMqtt.py", line 49, in discover
    discovered_devices = broadlink.discover(
  File "/Users/REDACTED/Documents/docker-python/broadlink_ac_mqtt/classes/broadlink/ac_db.py", line 127, in discover
    dev = gendevice(devtype, host, mac, name=name, cloud=cloud)
  File "/Users/REDACTED/Documents/docker-python/broadlink_ac_mqtt/classes/broadlink/ac_db.py", line 21, in gendevice
    return ac_db(host=host, mac=mac, name=name, cloud=cloud, devtype=devtype, update_interval=0)
  File "/Users/REDACTED/Documents/docker-python/broadlink_ac_mqtt/classes/broadlink/ac_db.py", line 352, in __init__
    if self.auth() == False:
  File "/Users/REDACTED/Documents/docker-python/broadlink_ac_mqtt/classes/broadlink/ac_db.py", line 184, in auth
    response = self.send_packet(0x65, payload)
  File "/Users/REDACTED/Documents/docker-python/broadlink_ac_mqtt/classes/broadlink/ac_db.py", line 237, in send_packet
    aes = AES.new(bytes(self.key), AES.MODE_CBC, bytes(self.iv))
  File "/Users/REDACTED/Documents/docker-python/.venv/lib/python3.10/site-packages/Crypto/Cipher/AES.py", line 95, in new
    return AESCipher(key, *args, **kwargs)
  File "/Users/REDACTED/Documents/docker-python/.venv/lib/python3.10/site-packages/Crypto/Cipher/AES.py", line 59, in __init__
    blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
  File "/Users/REDACTED/Documents/docker-python/.venv/lib/python3.10/site-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
    self._cipher = factory.new(key, *args, **kwargs)
SystemError: PY_SSIZE_T_CLEAN macro must be defined for '#' formats

Just a quick googling for this reveals the answer:

https://bugs.python.org/issue40943

Try to run it with python 3.9, and not with 3.10.

Edit:

Or look at this and update pycrypto to the mentioned python-crypto-2.6.1-33.fc34

https://bugzilla.redhat.com/show_bug.cgi?id=1897544