Blueair purifier addon

Hi!

I have a Blueair air purifier that I would love to get in to hassio!

There is already some phython projects that communicates with these products. Would it be possible to create an addon based on this?



Thanks!

Can someone help me get started with this? Pretty please? :pleading_face:

I’d be interested in this too.

Something new regarding this?

Could any dev please take a look?

I’d also be very much interested in this functionality.

Adding this note primarily for future searches, because I couldn’t find the answer (until I sprung for one). The Blueair Pure 211+ (350 CADR) and 121 (400 CADR) are their simpler line without connectivity, and only a single capacitive off -> low -> med -> high button. They are very quiet and reasonably priced. Best yet, the unit remembers the previous setting when power is lost, unlike many capacitive button device. I’m now controlling mine using simple Xiaomi Aqara Smart Plugs (or any smart plug of your choice).

The issue is these neither have nor provide access to built-in air quality sensors. Plus, I already have a couple of the other type of purifiers.

Not sure if it’s of interest to anyone here, but I came across this thread when trying to find an integration for my BlueAir 280i, and after a little more searching couldn’t find anything that did exactly what I wanted, so I whipped up a very simple sensor integration based on an existing python client for blueair.

Selfishly I didn’t need to control the devices, just read sensor values into the attributes of a HASS entity, so that’s all I’ve implemented so far, but if that’s what you need too this will do the trick.

2 Likes

Thank you, I’d like to give it a try. But as I am quite new to the platform I don’t know where to put the credentials

Sensor:
  - platform: blueair
    user: "<you>@gmail.com"
    password: "1337p@szw0Rd"

I installed the files in the config directory under custom_components but now I’ stuck. Maybe you could give me a hint. Thanks.

@aijayadams, this component works great! I just got a 280i and updated my home assistant with the custom_component details and voila! I see all the details:

What does it take to get this to be a supported integration? I’m thinking about buying a larger unit for the living room in my house but having this built in would make a difference…

EDIT: I also just realized that there is no control over it with this custom_component. :dizzy_face:

Thanks for putting it out there!

Okay, I’ve been trying to figure out how to modify this custom_component so the device can be controlled. On mylesagray’s repo I saw his app has the ability to control the purifier. He also graciously shared a Postman Collection so I installed Postman and tried it out. IT WORKS! I can control my Blueair 280i via post requests! Now I just need to integrate that into this custom_component.

I don’t have any experience with that and it seems that the structure is beyond me. I spent a few hours tonight pouring over the implementation of the existing component and comparing it to others that I have and some I found online as well as trying to understand the documentation.

As a first step, I’d like to take this implementation and break out all the sensor data into separate entities because right now they are all represented as attributes on a single entity. That should be a good stepping stone to get my feet wet in understanding the structure well enough to try to make a switch that can issue a post request to control the purifier.

However, I’m having a hard time understanding how to do that in the code. The code is structured pretty well in terms of having multiple purifiers and fetching all the data, each one will get added to HA via this block:

    ha_entities = []
    devices = api.get_devices()
    for dev in devices:
        ha_entities.append(
            BlueAirFilter(config=config, uuid=dev["uuid"], name=dev["name"], api=api)
        )

    add_entities(ha_entities)

The problem is, that results in a single update(self) definition that currently updates all attributes for the one entity. I still want a single get request to be issued but it should populate several sensors at once. From what I understand the sensors should be defined in separate classes but I can’t wrap my head around how to structure it properly after looking at all the simple examples. :confused:

None of the examples have a single request to fulfill more than one sensor’s worth of data.

Also, reading through the docs it seems there may be a better way to implement this since this is using the poll method of update() instead of async_update() but I still don’t know enough about it…

Looking for code structure guidance help here.

1 Like

@SpikeyGG glad there was some useful basis in there for you. At the time I didn’t have any need to control the unit so I just set it up as a sensor, the underlying library most definitely understands the controls, so exposing that to hass wouldn’t be too hard. Let me take a look and update the component.

As for becoming a standard builtin integration… I feel the hass community probably has a higher bar for testing and support than I am able to provide.

That would be awesome! I followed the instructions on this github site (instructions in the .devcontainer folder) to set up a development environment. Installed VSCode, set up Docker, got a debug Home Assistant instance running but then I ran into several problems with python and I’m still struggling to get the linter working in VSCode. It’s probably because I’m trying to set it up in a Windows 10 environment. :disappointed:

I know some Python and have built some cool stuff with it before but I’m not very good with code organization/structure. I’ve made some quick (and flat) snippets of code that can be used to send post commands to the device. It works with my 280i. I made the set_fan_speed function take speed as "auto" or a valid speed since all I want to do is fix it to one of the speeds or set it back to auto.

def get_devices(api_key, home_host, username, auth_token):
    response = requests.get(
        f"https://{home_host}/v2/owner/{username}/device/",
        headers={
            "X-API-KEY-TOKEN": api_key,
            "X-AUTH-TOKEN": auth_token,
        },
    )
    
    return response.json()

def set_fan_speed(api_key, home_host, auth_token, device_uuid, new_speed):
    if new_speed == "auto":
        new_name = "mode"
    elif new_speed in [0,1,2,3,"0","1","2","3"]:
        new_name= "fan_speed"
    else:
        raise Exception("Speed not supported")
    
    response = requests.post(
        f"https://{home_host}/v2/device/{device_uuid}/attribute/fanspeed/",
        json={
            "currentValue": str(new_speed),
            "scope": "device",
            "defaultValue": str(new_speed),
            "name": new_name,
            "uuid": str(device_uuid)
        },
        headers={
            "Content-Type": "application/json",
            "X-API-KEY-TOKEN": api_key,
            "X-AUTH-TOKEN": auth_token,
        },
    )
    
    return response

I execute it something like this:

my_devices = get_device_details(api_key, home_host, username, auth_token)
my_resp = set_fan_speed(api_key, home_host, auth_token, my_devices[0]['uuid'], 3)
time.sleep(10)
my_resp = set_fan_speed(api_key, home_host, auth_token, my_devices[0]['uuid'], "auto")

I also created a more updated version (v2.1) of the postman collection file to use for testing if you want it… just let me know and I can send it to you.

OK @SpikeyGG , so I looked at how the built-in integrations are structured I did some reorganizing to give us a device under which all the sensors can live as entities. This will address getting your discreet entities while still having a single fetch of the data per device.

I’ve also defined the config_flow so you can add and configure this integration in the GUI.

Screenshot 2021-09-12 132139 Screenshot 2021-09-12 125202

By storing the auth_token (in the client_api) on the device itself you should be able to take your control code and extend the FanEntity (or whatever you think is a good base) in a similar manner to the extensions I made to the SensorEntity.

Finally - just so there is no confusion, this is less tested, less stable and less complete then the simple sensor integration on the main branch. It is however more inline with how modern hass integrations are structured and if you have cycles to help fill out the remaining sensors and fan control (and whatever else blueair support through their API) that would be super. Feel free to send pull requests on github :wink:

1 Like

Basic fan control added to the blueair_device branch :slight_smile:

FanControl

1 Like

Woah!! That’s awesome! Thanks @aijayadams!

No worries at all, now that the basic structure is in place there is still a lot of room for improvement. I’d welcome any pull requests :sunny:

And just a caution to anyone changing from the old sensor platform version to this one, there is a recent commit I’m depending on for the device classes, so make sure your installation is up to date or you will just get the fan and the sensors will error out :wink:

Hah, I pulled it down while you were reorganizing the repo and hacked in some stuff. Then I realized you created a cleaner version of all the stuff I hacked in so I threw it away and pulled in your changes. This is excellent! I noticed right away that the units are missing. I’ve been trying to figure out how to push a new branch into your repo but I think it might be read only or I’m doing it wrong.

(base) spikeygg@hassio:~/src/hass-blueair$ git push origin add_units:add_units
ERROR: Permission to aijayadams/hass-blueair.git denied to spikeygg.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
(base) spikeygg@hassio:~/src/hass-blueair$

Anyway, the changes I made were very simple. On all the sensors you added in sensor.py you’ll need _attr_native_unit_of_measurement = "µg/m³" for the PM ones and _attr_native_unit_of_measurement = "ppb" for the others (I think).

Without the units defined, Home Assistant thinks they’re attributes and won’t actually create a line chart – if you should want that. I DO! :smiley:

Thanks again for your work on this!

1 Like

Oh that’s just how GitHub works, you’ll need to fork the repo first, and push your changes to your fork, from there you create a pull request back to my copy and your changes get applied :slight_smile: