Custom Component - How to create multiple sensors?

I’ve created a custom sensor component following the instructions on the home assistant website.
It queries a web-site and retrieves a bunch of information that I’ve currently saved as attributes.
I’d like to make some of the data available as standalone sensors - but it doesn’t make sense to duplicate my component and query the website multiple times choosing to save a different value as the ‘state’.

My Ring doorbell creates half a dozen sensors. WeatherUnderground creates many sensors.

I can’t find any documentation that explains how to do this from a custom component. Can anyone help me out by explaining how to do this - or pointing me at some documentation that explains it? I can’t imagine that it’s all that difficult compared to creating the initial component…… is it?

Thanks.

I’ve been wondering the same thing.
I have not been able to work it out yet, but I’m trying to get some inspiration from hub components that implement several other components, such as the Tesla component. It creates several sensors and switches when included in the config file.

So far it looks to me like the Tesla component uses discovery…:

 TESLA_COMPONENTS = [
     'sensor', 'lock', 'climate', 'binary_sensor', 'device_tracker', 'switch'
 ]

 for component in TESLA_COMPONENTS:
         discovery.load_platform(hass, component, DOMAIN, {}, base_config)

There is some info on discovery in the dev pages, but I can’t see how this info is related to the use above:
https://developers.home-assistant.io/docs/en/creating_component_discovery.html

I really wish there were more info/guides/tutorials on how to make custom components. Maybe it all goes over my head, but the dev pages are practically useless to me…

1 Like

The components you use as examples will possibly be an education as to how to do it:

eg https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/sensor/wunderground.py

@nickrout that’s quite a bit of code, and no call to discovery.load_platform… In your opinion, which part specifically is responsible for instantiating the extra components?

LOL I don’t say I know the answer, I was just trying to point to an example.

Yes the wunderground is probably a complicated example. Something simpler but which has multiple sensors is the xiaomi_aqara switch. https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/sensor/xiaomi_aqara.py

As I say, I am no expert, but I can see lines like

SENSOR_TYPES = {
'temperature': [TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE],  (etc)

I should also point this out:

sensors = ['sensor1', 'sensor2', 'sensor3']
dev = []
for sensor in sensors:
  dev.append(SensorClass(sensor))
add_devices_callback(dev, True)

How would i be able to update the sensor state from outside the sensor, i’m pretty sure what was meant is that he has created a sensor which recieves a bunch of info from an API and right now (like me) puts it in the attributes.
So in case of a solar inverter sensor i’m attempting to build my state is 371.5 (W) and my attributes are

vpv1: 195.9
ipv1: 1.7
ppv1: 352
vpv2: 156
ipv2: 0.1
ppv2: 27.1
vpv3: 0
ipv3: 0
ppv3: 0
ppv: 62.5
unit_of_measurement: W
friendly_name: Growatt

Where the state is the full output of the solar pannels and attribute ppv1 and ppv2 are what the pannels are doing on the front and side of my house.

However ppv3 isn’t connected or exists so i’d want to create sensors for the front and side solar pannels but not have to request the information from the server 3 times for the total, front pannels and side pannels.

You are looking for templating in the docs.,

Do you mean a template sensor? This is what i’m using as a temporary solution but i don’t think it is a proper way for a component to work. It should add the required sensor entries automatically.

Use a data class in the sensor to fetch new data.
Store that data in hass.data and have each sensor variation get the value from hass.data

Example implementations:

Ah so it’s using a class to get the data from what it seems. As far as i can see though it doesn’t cache this data but is fetching it new every time, but i could build something like that myself.

Thank you for this suggestion, i’ll hopefully try it out tomorrow.

Edit:
Never mind the caching part, i missed the whole
@Throttle(TIME_BETWEEN_UPDATES)
So does this mean it will return one of the previously retrieved pieces of data?

Store the data in hass.data.
in your update method you first call the update method under the data class.
This will update hass.data with new info if the throttle limit have passed.

then fetch the data from hass.data.

minified example:

MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10)
DOMAIN_DATA = "awesome_data"

async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    data = AwesomeData(hass)
    sensor_type = 'ppv2'
    sensor = [AwesomeSensor(data, sensor_type)]
    async_add_entities(sensor, True)

class AwesomeSensor(Entity):

    async def async_update(self):
        await self.data.update()
        self._state = self.data[DOMAIN_DATA][self.sensor_type]

class AwesomeData()

    @Throttle(TIME_BETWEEN_UPDATES)
    async def update(self):
        new_data = #get data here
        self.hass.data[DOMAIN_DATA] = new_data # set data

that will not work, but you should get the general idea

I know this is a tad old, but I’m trying to add a couple of input_number entities in a custom component. Using the suggestion above for adding sensors, I tried this for an input_number:

input_numbers = ["htc_test"]
dev = []
for input_number in input_numbers:
    dev.append(InputNumberClass(input_number))
add_devices_callback(dev, True)

But got the error: NameError: name 'InputNumberClass' is not defined

So then I went back and tried the SensorClass approach, and that created the same error (except SensorClass). After digging into the reference for pi hole above, I found the statement async_add_entities. It appears to add a list of classes. In the pi hole example, it’s:

    sensors = [PiHoleSensor(pi_hole, name, condition)
               for condition in config[CONF_MONITORED_CONDITIONS]]

    async_add_entities(sensors, True)

The first list entry, PiHoleSensor(...) is the class name. The args (pi_hole, name, condition) are the args sent to the class. So… My assumption is that the sensors = creates an instance of the class, and then adds that to the HA list of entities (and does this for each condition in this case).

This creates a couple more questions (and I thought I was decent at Python!):

  • What actually tells HA this is a sensor and not, for example, an input_boolean?
  • Do we have to create a class for each entity we want to create? If so, what would that class look like for an input_number?

Thanks for any help anyone can offer!