Component for Bring! Shopping List

It would be great to pull in shopping lists etc… from the Bring! app which is one of the best shopping list apps for iOS/Android.

It appears to integrate with Google/Alexa already

I got this at the moment, a lot of hardcoded things

6 Likes

Looks very good. Is there any information on how this came about?

I am experimenting myself with node-red and some codes I found:

–> It’s a shame that Bring! has no open API.

This is my sensor

from homeassistant.helpers.entity import Entity
from requests import get

ICON = "mdi:cart"
ICONEMPTY = "mdi:cart-outline"
LIST_ID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"


def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the sensor platform."""
    add_entities([BringSensor()])


class BringSensor(Entity):
    """Representation of a Sensor."""

    def __init__(self):
        """Initialize the sensor."""
        self._state = None
        self._purchase = []
        self._recently = []

    @property
    def name(self):
        """Return the name of the sensor."""
        return 'Shopping List'

    @property
    def state(self):
        """Return the state of the sensor."""
        return self._state

    @property
    def icon(self):
        """Return the icon to use in the frontend, if any."""
        return ICON if len(self._purchase) > 0 else ICONEMPTY

    @property
    def device_state_attributes(self):
        """Return the state attributes."""
        attrs = {}
        attrs["Purchase"] = self._purchase
        attrs["Recently"] = self._recently
        return attrs

    def getList(self, source=[], details=[], articles=[]):
        items = []
        for p in source:
            found = False
            for d in details:
                if p["name"] == d["itemId"]:
                    found = True
                    break

            item = {}
            item["image"] = p["name"]
            item["name"] = p["name"]
            item["specification"] = p["specification"]

            if found == True:
                item["image"] = d["userIconItemId"]

            item["key"] = item["image"]

            if(item["name"] in articles):
                item["name"] = articles[item["name"]]
            else:
                if(found == 0):
                    item["image"] = item["name"][0]

            item["image"] = item["image"].lower().replace(
                "é", "e").replace("ä", "ae").replace("-", "_").replace("ö", "oe")

            # print("%s: %s => %s" % (item.key, item.name, item.specification))

            if "+" in item["specification"]:
                specs = item["specification"].split("+")

                for spec in specs:
                    temp = dict(item.items())
                    temp["specification"] = spec.strip()
                    items.append(temp)

            else:
                items.append(item)

        return items

    def update(self):
        self._items = []
        self._recently = []
        """Fetch new state data for the sensor.
        This is the only method that should fetch new data for Home Assistant.
        """
        # get articles US
        url = "https://web.getbring.com/locale/articles.en-US.json"
        articles = get(url=url).json()

        url = f"https://api.getbring.com/rest/bringlists/{LIST_ID}/details"
        details = get(url=url).json()

        url = f'https://api.getbring.com/rest/bringlists/{LIST_ID}'
        data = get(url=url).json()

        purchase = data["purchase"]
        recently = data["recently"]

        self._purchase = self.getList(purchase, details, articles)
        self._recently = self.getList(recently, details, articles)

        self._state = len(purchase)

2 Likes

and this is my custom card

const LitElement = Object.getPrototypeOf(
  customElements.get("hui-view")
);

const html = LitElement.prototype.html;

class BringShoppingList extends LitElement {
  static get properties() {
    return {
      hass: {},
      config: {}
    };
  }
  constructor() {
    super();
    this.config = {};
  }

  renderStyle() {
    return html`
    <style>
    </style>
    `
  }

  render() {
    if (!this.config || !this.hass) {
      return html``;
    }

    const stateObj = this.hass.states[this.config.entity];

    return html`
      ${this.renderStyle()}
      <ha-card .header="${this.config.name}">        
        <div class="card-content">
          ${stateObj.attributes.Purchase.map(item => this.getItemTemplate(item, this.config.purchaseColor))}
        </div>
        ${this.config.showRecent ? html`
          <div class="card-content">
            ${stateObj.attributes.Recently.map(item => this.getItemTemplate(item, this.config.recentlyColor))}
          </div>
        ` : ""}        
      </ha-card>
    `;
  }

  getItemTemplate(item, color) {
    return html`
    <paper-button
    style="display: inline-block;
                    background-color: ${color};
                    color: #f4f4f4;
                    margin: 2px;
                    padding: 10px 5px;
                    height: ${this.config.itemHeight}px;
                    width: ${this.config.itemWidth}px;
                    cursor: pointer;
                    text-align: center;"
          .key="${item.key}"
          @click="${this.handleActionClick}"              
        >
        <div style="width: auto; height: ${this.config.itemHeight / 2}px">
          <img  style="max-width: 100%;
                      max-height: 100%;" 
                src="https://web.getbring.com/assets/images/items/${item.image}.png">
        </div>
        <label style="display: block;
                      text-align: center;
                      color: #FFF;
                      font-size: 0.8em;
                      cursor: pointer;"
        >${item.name}</label>
        <label style="display: block;
                      text-align: center;
                      color: #FFF;
                      font-size: 0.8em;
                      cursor: pointer;"
        >${item.specification || html`&nbsp;`}</label>  
    </paper-button>
    `
  }

  handleActionClick(e) {
    const theme = e.currentTarget.key;
    this.hass.callService(
      "bring",
      "swap_item",
      {
        entityId: this.config.entity,
        key: theme
      }
    );
  }

  getCardSize() {
    return 9;
  }

  setConfig(config) {
    if (!config.entity)
      throw new Error('Please define an entity');

    if (this.lastChild) this.removeChild(this.lastChild);
    this.config.entity = config.entity;
    this.config.name = config.name;
    this.config.showRecent = config.show_recents == true ? true : false;

    this.config.purchaseColor = '#ee524f';
    this.config.recentlyColor = '#4faba2';

    this.config.itemWidth = 90;
    this.config.itemHeight = 63;

    if (typeof config.purchase_color !== 'undefined' && config.purchase_color !== null)
      this.config.purchaseColor = config.purchase_color;

    if (typeof config.recently_color !== 'undefined' && config.recently_color !== null)
      this.config.recentlyColor = config.recently_color;

    if (typeof config.item_width !== 'undefined' && config.item_width !== null)
      this.config.itemWidth = config.item_width;

    if (typeof config.item_height !== 'undefined' && config.item_height !== null)
      this.config.itemHeight = config.item_height;

  }
}

customElements.define('bring-shopping-list-card', BringShoppingList);

console.info(
  `%c BRING-SHOPPING-LIST   \n%c     Version 0.0.1     `,
  "color: orange; font-weight: bold; background: black",
  "color: white; font-weight: bold; background: dimgray"
);
3 Likes

I am new here in the forum, your contribution is exactly what I intend.
Can you tell me how you integrated the list? And where did you get the “Liste_id” from?
Where do I have to enter your codes exactly? Unfortunately it doesn’t work with sensors.

I’m not a developer myself but I’d like to explain what I’ve tried so far. It doesn’t work for me yet. Maybe it helps others and we get the solution from @dotKrad closer.

The first file is the bring.py as custom component. The second then saved as custom card: bring.js

I got the List-ID from the developer tools of Chrome. For this a picture:

If I set this one here I get a value back:

https://api.getbring.com/rest/bringlists/{LIST_ID}/details

I come from Germany so I had to adapt the following:

https://web.getbring.com/locale/articles.de-DE.json (instead of en-US) --> works

With the YAML entry I started like a noob with :

sensor:
  - platfrom: bring

It doesn’t work yet but I would like to thank @dotKrad for the files and the support.

1 Like

dotKrad can you tell me how to integrate the sensor and the custom card? in configuration.ymal i only get error messages.

Hey guys, I uploaded the code I actually have for Bring integration
I’m not an expert, feel free to contribute

Component
Card

You need to add this to your configuration.yaml

bring_shopping_list:
  lists:
    - id: xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      name: Home
      locale: de-DE
    - id: xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      name: Work

sorry guys, I haven’t write the component documentation yet, but you need to copy the files in a bring_shopping_list folder under custom_components

5 Likes

Thanks, now it works :slight_smile:

I am not able to change the status of a product.
Is there any way through the API or Lovelace UI?

Holy thread revivial Batman!
So, I have;

  • created the files in custom components
  • created folder www/custom-lovelace/ and placed the file in there
  • added to my configuration.yaml as per dotKRAD two posts up

When I try to add a card via lovelace GUI I have no card…?
I should aslo note that I did not have the custom-lovelace folder but did have a community folder in www - funnily enough, the three in there (dual guage, spotify, calendar) are also missing from the GUI.

Can someone point me in the right direction…?

Sorry,
Dave the n00b.

Ignore me. I’m an idiot. Moved lovelace to yaml.

Hi … i would like to add the Bring list. Sorry for my noob question (i’m new in HA).

Could you may help me. I did the steps from the above post. Now i got:

Component Error: bring_shopping_list - Integration "bring shopping list" not found

Custom element doesn’t exist: bring-shopping-list-card.

Thx

Hi
after struggling some hours with the card integration the lists are now shown in my lovelace-ui but there is no click action and I don’t see the recently items. is this a known limitation or do I’ve configure something more?
BR Stefan

And additional for iobroker there is a node API: https://github.com/foxriver76/node-bring-api/blob/b91e35e6aa297c58c560e919d85192da3180e9b7/lib/bring.js maybe this helps to develop it further?
BR Stefan

Sorry guys, I had to stop the develop of this component, cause I’m really busy at the moment.
@stefankumli you can show the recent items in the card, setting show_recents: true

currently there are no click actions, just showing the items in the list
Hope can continue developing it as soon as possible

1 Like

I have made a “gadget” to add products to the Bring! app.
Link…

3 Likes

This is great work - please do keep it up. As original post, this is one of the most popular apps for shopping lists, so be awesome to have a slick integration in home assistant!

Hello guys

I managed to install this via HACS, it seems everything is setup fine, but when I add the card to the UI I have this:

I copied and pasted the config as it is shown in the github page, but there’s no way to get this working. I double checked the list IDs as well. What could be wrong ?