Custom component light always goes to state 'on'

This has been driving me crazy, so I figured I’d start here

I have written a few, very simple custom components that work great. One of those is for this fan light I have, which talked to a raspberry pi which used some RF transmitter to turn the light on and off. That works great to this day

I was re-writing the component to be more generic for another device that will be controllable via the raspberry pi (called it a PiBridge). This time, i used async io, where as my previous component wasn’t doing async.

The issue I’m running into is that while my component works (light turns on and off as I flip the state in lovelace) the lights state, once it goes to ‘on’/True, stays ‘on’/True in the front end. I’ve scoured some examples but I can’t figure it out. Some example register callbacks, some don’t, and they all seem to work. I know my Light object’s state is correct (based on Logger prints) but for whatever reason HA thinks the state is always ‘on’

I’ve mucked with the code so much to figure it out, so excuse the ugliness. It’s not that complicated, which is what is driving me crazy.

async def async_setup_entry(hass, config_entry, async_add_entities):
    """Setup a Light Entity controlled by the PiBridge"""
    _LOGGER.info("Setting up light entry")
    bridge: PiController = hass.data[DOMAIN][config_entry.entry_id]
    lightDevices = await bridge.async_get_devices("light")
    _LOGGER.info("Devices found in setup_entry: {}".format(lightDevices))
    for dev in lightDevices:
        _LOGGER.info("{}: {}".format( dev.get("devId", None), dev.get("state", False)))
    newLights = [PiLight(hass, bridge, dev.get("devId", None), dev.get("state", False)) for dev in lightDevices]

    #component = EntityComponent(_LOGGER, DOMAIN, hass)
    #component.async_register_entity_service(SERVICE_SYNC, {}, "async_do_sync")
    platform = entity_platform.async_get_current_platform()
    platform.async_register_entity_service(
        SERVICE_SYNC,
        {
        },
        "async_do_sync",
    )

    async_add_entities(newLights, update_before_add=True)   
    return True


class PiLight(LightEntity):
    def __init__(self, hass, bridge, devid, state) -> None:
        _LOGGER.info("adding light: {} {}".format(devid, state))
        self.bridge: PiController = bridge
        self.hass = hass
        self._state = state
        self._unique_id = devid
        self.devid = devid
        #self._attr_brightness = 0

    @property
    def is_on(self):
        _LOGGER.info(f"SOMEONE ASKED OUR STATE {self._state}")
        return self._state

    @property
    def supported_features(self):
        return 0

    @property 
    def unique_id(self):
        return self._unique_id

    @property
    def should_poll(self):
        return False
        
    @property
    def available(self) -> bool:
        """Return True if entity is available."""
        return True

    # async def async_update(self) -> None:
    #     #resp,state = await self.bridge.async_send_get("state", data_dict={"devId": self.devid})
    #     #_LOGGER.info(f"Update called: {resp}, {state}")
    #     #if not resp:
    #     #    _LOGGER.error(f"Failed to get light state {resp}")
    #     #    return
    #     #_LOGGER.info(f"Current state: {self._state}, light state on other side: {state}")
    #     #self._attr_is_on = state
    #     #self._state = state
    #     #self._attr_brightness = 0
    #     if self.entity_id is None:
    #         _LOGGER.info("Entity ID was none, skipping update")
    #         return
    #     self.async_schedule_update_ha_state()

    async def async_turn_on(self, **kwargs):
        _LOGGER.info(f"Turning light on. Current State {self._state} id is {self.entity_id}")
        _LOGGER.info(f"Light on args: {kwargs}")
        if not self._state:
            _LOGGER.info(dir(self.bridge))
            resp, temp = await self.bridge.async_send_get("flip", data_dict={"devId": self.devid})
            _LOGGER.info(f"Got back {resp}, {temp}")
            self._state = temp
            self.async_write_ha_state()

    async def async_turn_off(self, **kwargs):
        _LOGGER.info(f"TURN OFF id is {self.entity_id}")
        _LOGGER.info("Turning light off. Current State {} {}".format(self._state, self.devid))
        if self._state:
            _LOGGER.info(dir(self.bridge))
            _, resp = await self.bridge.async_send_get("flip", data_dict={"devId": self.devid})
            _LOGGER.info(f"Turn off sent, got back {resp}")
            self._state = resp
            #if not self._state:
                #self._attr_brightness = 0
            self.async_write_ha_state()
    async def async_do_sync(self):
        #Someone told us we were off
        await self.bridge.sync(self.devid)
        #await self.async_update()
        #self.async_write_ha_state()

EDIT: made should_poll return False, I had tried True without luck

It looks like you are setting self._state to a response value instead of true/false. As such, it is likely that it is always a non falsy value so is_on is therefore always true.

Should add, if the response is ‘true’ or ‘false’ this maybe a string and not a boolean and as such always a true value.

You the man. I was so wrapped up in thinking I was doing async wrong or missing something new that my older components never used that I missed the answer haha.

Thanks so much!