How to rename a state

Hi guys, I’m kinda new, and I would appreciate your help.

I have a broadlink S1C alarm kit, which I successfully added to my HA. The issue, The state is open or closed! they are working fine, but I would like to see the states in my language (portuguese). Instead of open, Id like to see “aberto” and “fechado” to closed.

how I added this sensor? few lines in my configurarion.yaml and I got a “py file” and added to my custom_components folder.

This is how I see:
windows

This is my configuration.yaml, about the sensors: (I have a broadklink rm pro temperature sensor, my raspberry and the s1c alarm kit (I just need the door sensors):

And this is my customize.yaml, which I only changed the icon and put a friendly name
cust_sensor

I tried the code below 100 times, different ways but still nothing. No errors but no functional either.
sensor:

  • platform: template
    sensors:
    window_1:
    friendly_name: “Window 1”
    value_template: >-
    {% if is_state(‘sensor.broadlink_s1c_janela_principal’, ‘open’) %}
    Aberto
    {% else %}
    Fechado
    {% endif %}

Please Format you codeso we can fix it

think it to do with the formating spaceing

1 Like

what does that mean exactly?
could you format your template sensor’s config properly in the original post (by enclosing it in three back quotes) and also add output of the following when pasted into Template editor:

{{ states('sensor.broadlink_s1c_janela_principal') }}

Sorry guys, here´s my code. I did exactly what other people did and it worked for them.
When I said “no errors but no functional either”, I meant no errors when I check configuration, it says “valid configuration” and no functional because states keep appearing as open and closed.

Thank you all for your help! I’m still a newbie, still learning how this whole thing works.

CONFIGURATION.YAML

sensor:
  - platform: broadlink # RM Pro Temperature
    scan_interval: 300
    host: 192.168.0.42
    mac: 'B4:43:0D:38:BB:1C'
    monitored_conditions:
      - 'temperature'
  - platform: systemmonitor # raspberry
    resources:
      - type: memory_use_percent
      - type: processor_use
      - type: disk_free
        arg: "/"
      - type: disk_use
        arg: "/"
      - type: network_in
        arg: eth0
      - type: network_out
        arg: eth0
  - platform: broadlink_s1c # Broadlink S1c, only using 2 door (window) sensors
    ip_address: 192.168.0.43
    mac: "34:EA:34:B6:6F:BF"
    timeout: 10
  - platform: template # customizing states
    sensors:
      janela_principal: # rename state of "main window" open >> aberta, closed >> fechada
        value_template: '{% if is_state("sensor.broadlink_s1c_janela_principal", "open") %}Aberta{% else %}Fechada{% endif %}'
      janela_lateral: # rename state of "window near the A/C" open >> aberta, closed >> fechada
        value_template: '{% if is_state("sensor.broadlink_s1c_janela_ac", "open") %}Aberta{% else %}Fechada{% endif %}'

CUSTOMIZE.YAML

sensor.broadlink_s1c_janela_principal:
  entity_picture: /local/window2.png
  friendly_name: Janela Principal
sensor.broadlink_s1c_janela_ac:
  entity_picture: /local/window2.png
  friendly_name: Janela Lateral

This is the code from my sensor.py (I just got it from internet, havent changed anything) but i guess this is a frontend issue… well, this is the code anyway:

CUSTOM_COMPONENTS\BROADLINK_S1C\SENSOR.PY

"""////////////////////////////////////////////////////////////////////////////////////////////////
Home Assistant Custom Component for Broadlink S1C Alarm kit integration as a Sensor platform.
Build by TomerFi
Please visit https://github.com/TomerFi/home-assistant-custom-components for more custom components
if error occures, raise the log level to debug mode and analyze the logs:
                    custom_components.sensor.broadlink_s1c: debug
installation notes:
place this file in the following folder and restart home assistant:
/config/custom_components/sensor
yaml configuration example:
sensor:
  - platform: broadlink_s1c
    ip_address: "xxx.xxx.xxx.xxx" # set your s1c hub local ip address
    mac: "XX:XX:XX:XX:XX:XX" # set your s1c hub mac address
////////////////////////////////////////////////////////////////////////////////////////////////"""
import binascii
import socket
import datetime
import logging
import asyncio
import traceback
import json
import threading

import voluptuous as vol

from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (CONF_IP_ADDRESS, CONF_MAC, CONF_TIMEOUT, STATE_UNKNOWN, STATE_OPEN, STATE_CLOSED,
    EVENT_HOMEASSISTANT_STOP, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY)
from homeassistant.util.dt import now

"""current broadlink moudle in ha is of version 0.5 which doesn't supports s1c hubs, usuing version 0.6 from github"""
# REQUIREMENTS = ['https://github.com/mjg59/python-broadlink/archive/master.zip#broadlink==0.6']
"""one of the broadlink 0.6 requirements is the pycrypto library which is blocked ever since HA 0.64.0, my forked repository of python-broadlink is working with its replacement pycryptodome"""
# REQUIREMENTS = ['https://github.com/TomerFi/python-broadlink/archive/master.zip#broadlink==0.6']
"""home assistant 0.67.1 is using broadlink 0.8 so there is no need for special requirements"""
REQUIREMENTS = []

_LOGGER = logging.getLogger(__name__)

"""platform specifics"""
DOMAIN = 'sensor'
ENTITY_ID_FORMAT = DOMAIN + '.broadlink_s1c_{}'
DEFAULT_TIMEOUT = 10

"""additional states that doesn't exists in homeassistant.const"""
STATE_NO_MOTION = "no_motion"
STATE_MOTION_DETECTED = "motion_detected"
STATE_TAMPERED = "tampered"
STATE_ALARM_SOS = "sos"

"""sensor update event details"""
UPDATE_EVENT = "BROADLINK_S1C_SENSOR_UPDATE"
EVENT_PROPERTY_NAME = "name"
EVENT_PROPERTY_STATE = "state"

"""sensor types and icons"""
SENSOR_TYPE_DOOR_SENSOR = "Door Sensor"
SENSOR_TYPE_DOOR_SENSOR_ICON = "mdi:door"
SENSOR_TYPE_MOTION_SENSOR = "Motion Sensor"
SENSOR_TYPE_MOTION_SENSOR_ICON = "mdi:walk"
SENSOR_TYPE_KEY_FOB = "Key Fob"
SENSOR_TYPE_KEY_FOB_ICON = "mdi:remote"
SENSOR_DEFAULT_ICON = "mdi:security-home"

"""platform configuration schema"""
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_IP_ADDRESS): cv.string,
    vol.Required(CONF_MAC): cv.string,
    vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int
})

"""set up broadlink s1c platform"""
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):

    _LOGGER.debug("starting platform setup")

    """get configuration params"""
    ip_address = config.get(CONF_IP_ADDRESS)
    mac = config.get(CONF_MAC).encode().replace(b':', b'')
    mac_addr = binascii.unhexlify(mac)
    timeout = config.get(CONF_TIMEOUT)

    """initiate connection to s1c hub"""
    conn_obj = HubConnection(ip_address, mac_addr, timeout)

    """discovering the sensors and initiating entities"""
    raw_data = conn_obj.get_initial_data()
    sensors = []
    for i, sensor in enumerate(raw_data["sensors"]):
        sensors.append(S1C_SENSOR(hass, sensor["name"], sensor["type"], conn_obj.parse_status(sensor["type"], str(sensor["status"])), now()))
    if sensors:
        async_add_devices(sensors, True)

    """starting the sensors status change watcher"""
    WatchSensors(hass, conn_obj).start()

    return True


class S1C_SENSOR(Entity):
    """representation of the sensor entity"""
    def __init__(self, hass, name, sensor_type, status, last_changed):
        """initialize the sensor entity"""
        self.entity_id = ENTITY_ID_FORMAT.format(name.replace(' ', '_').replace('-', '_').lower())
        self._hass = hass
        self._name = name
        self._sensor_type = sensor_type
        self._state = status
        self._last_changed = last_changed
        """registering entity for event listenting"""
        hass.bus.async_listen(UPDATE_EVENT, self.async_event_listener)
        _LOGGER.debug(self._name + " initiated")

    @property
    def name(self):
        """friendly name"""
        return self._name

    @property
    def should_poll(self):
        """entity should be polled for updates"""
        return False

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

    @property
    def icon(self):
        """sensor icon"""
        if (self._sensor_type == SENSOR_TYPE_DOOR_SENSOR):
            return SENSOR_TYPE_DOOR_SENSOR_ICON
        elif (self._sensor_type == SENSOR_TYPE_KEY_FOB):
            return SENSOR_TYPE_KEY_FOB_ICON
        elif (self._sensor_type == SENSOR_TYPE_MOTION_SENSOR):
            return SENSOR_TYPE_MOTION_SENSOR_ICON
        else:
            return SENSOR_DEFAULT_ICON


    @property
    def device_state_attributes(self):
        """sensor state attributes"""
        return {
            "sensor_type": self._sensor_type,
            "last_changed": self._last_changed
        }

    @asyncio.coroutine
    def async_event_listener(self, event):
        """handling incoming events and update ha state"""
        if (event.data.get(EVENT_PROPERTY_NAME) == self._name):
            _LOGGER.debug(self._name + " received " + UPDATE_EVENT)
            self._state = event.data.get(EVENT_PROPERTY_STATE)
            self._last_changed = event.time_fired
            yield from self.async_update_ha_state()


class HubConnection(object):
    """s1c hub connection and utility class"""
    def __init__(self, ip_addr, mac_addr, timeout):
        """initialize the connection object"""
        import broadlink
        self._hub = broadlink.S1C((ip_addr, 80), mac_addr, None)
        self._hub.timeout = timeout
        self._authorized = self.authorize()
        if (self._authorized):
            _LOGGER.info("succesfully connected to s1c hub")
            self._initial_data = self._hub.get_sensors_status()
        else:
            _LOGGER.error("failed to connect s1c hub, not authorized. please fix the problem and restart the system")
            self._initial_data = None

    def authorize(self, retry=3):
        """authorize connection to s1c hub"""
        try:
            auth = self._hub.auth()
        except socket.timeout:
            auth = False
        if not auth and retry > 0:
            return self.authorize(retry-1)
        return auth

    def get_initial_data(self):
        """return initial data for discovery"""
        return self._initial_data

    def get_hub_connection(self):
        """return the connection object"""
        return self._hub

    def parse_status(self, sensor_type, sensor_status):
        """parse sensors status"""
        if sensor_type == SENSOR_TYPE_DOOR_SENSOR and sensor_status in ("0", "128"):
            return STATE_CLOSED
        elif sensor_type == SENSOR_TYPE_DOOR_SENSOR and sensor_status in ("16", "144"):
            return STATE_OPEN
        elif sensor_type == SENSOR_TYPE_DOOR_SENSOR and sensor_status == "48":
            return STATE_TAMPERED
        elif sensor_type == SENSOR_TYPE_MOTION_SENSOR and sensor_status in ("0", "128"):
            return STATE_NO_MOTION
        elif sensor_type == SENSOR_TYPE_MOTION_SENSOR and sensor_status == "16":
            return STATE_MOTION_DETECTED
        elif sensor_type == SENSOR_TYPE_MOTION_SENSOR and sensor_status == "32":
            return STATE_TAMPERED
        elif sensor_type == SENSOR_TYPE_KEY_FOB and sensor_status == "16":
            return STATE_ALARM_DISARMED
        elif sensor_type == SENSOR_TYPE_KEY_FOB and sensor_status == "32":
            return STATE_ALARM_ARMED_AWAY
        elif sensor_type == SENSOR_TYPE_KEY_FOB and sensor_status == "64":
            return STATE_ALARM_ARMED_HOME
        elif sensor_type == SENSOR_TYPE_KEY_FOB and sensor_status in ("0", "128"):
            return STATE_ALARM_SOS
        else:
            _LOGGER.debug("unknow status " + sensor_status + "for type " + sensor_type)
            return STATE_UNKNOWN


class WatchSensors(threading.Thread):
    """sensor status change watcher class"""
    def __init__(self, hass, conn_obj):
        
        threading.Thread.__init__(self)
        
        """initialize the watcher"""
        self._hass = hass
        self._ok_to_run = False
        self._conn_obj = conn_obj
        self._last_exception_dt = None
        self._exception_count = 0
        if (self._conn_obj._authorized):
            self._ok_to_run = True
            self._hub = self._conn_obj.get_hub_connection()

    def run(self):
        """register stop function for event listening"""
        self._hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, self.stop)
        
        """get initial sensors data"""
        if not (self._conn_obj.get_initial_data() is None):
            old_status = self._conn_obj.get_initial_data()
        else:
            old_status = self._hub.get_sensors_status()
        
        """start watcher loop"""
        _LOGGER.info("starting sensors watch")
        while self._ok_to_run:
            try:
                current_status = self._hub.get_sensors_status()
                for i, sensor in enumerate(current_status["sensors"]):
                    current_fixed_status = self._conn_obj.parse_status(sensor["type"], str(sensor["status"]))
                    previous_fixed_status = self._conn_obj.parse_status(old_status["sensors"][i]["type"], str(old_status["sensors"][i]["status"]))
                    if not (current_fixed_status == previous_fixed_status):
                        _LOGGER.debug("status change tracked from: " + json.dumps(old_status["sensors"][i]))
                        _LOGGER.debug("status change tracked to: " + json.dumps(sensor))
                        self.launch_state_change_event(sensor["name"], current_fixed_status)
                        old_status = current_status
            except:
                _LOGGER.warning("exception while getting sensors status: " + traceback.format_exc())
                self.check_loop_run()
                continue
        _LOGGER.info("sensors watch done")

    def check_loop_run(self):
        """max exceptions allowed in loop before exiting"""
        max_exceptions_before_stop = 50
        """max minutes to remmember the last excption"""
        max_minutes_from_last_exception = 1
        
        current_dt = now()
        if not (self._last_exception_dt is None):
            if (self._last_exception_dt.year == current_dt.year and self._last_exception_dt.month == current_dt.month and self._last_exception_dt.day == current_dt.day):
                calc_dt = current_dt - self._last_exception_dt
                diff = divmod(calc_dt.days * 86400 + calc_dt.seconds, 60)
                if (diff[0] > max_minutes_from_last_exception):
                    self._exception_count = 0
                else:
                    self._exception_count += 1
            else:
                self._exception_count = 0
        else:
            self._exception_count = 0

        if not (max_exceptions_before_stop > self._exception_count):
            _LOGGER.error("max exceptions allowed in watch loop exceeded, stoping watch loop")
            self._ok_to_run = False

        self._last_exception_dt = current_dt

    def stop(self, event):
        """handle stop request for events"""
        _LOGGER.debug("received :" + event.event_type)
        self._ok_to_run = False

    def launch_state_change_event(self, name, status):
        """launch events for state changes"""
        _LOGGER.debug("launching event for " + name + "for state changed to " + status)
        self._hass.bus.fire(UPDATE_EVENT,
            {
                EVENT_PROPERTY_NAME: name,
                EVENT_PROPERTY_STATE: status
            })

Please post what @AhmadK requested.

We also need to see your Lovelace card’s code featuring the template sensor (but no python code).

Sorry, like I said, I’m still a newbie.
Is this what you need? If not, please could you tell me how to get what you are asking for?

entities:
  - entity: sensor.broadlink_s1c_janela_principal
  - entity: sensor.broadlink_s1c_janela_ac
  - entity: sensor.broadlink_sensor_temperature
show_header_toggle: false
title: Sensores - Sala
type: entities

source:

is this what you need?

  - platform: template # customizing states
    sensors:
      janela_principal: # rename state of "main window" open >> aberta, closed >> fechada
        value_template: '{% if is_state("sensor.broadlink_s1c_janela_principal", "open") %}Aberta{% else %}Fechada{% endif %}'
        friendly_name: 'Janela principal'
      janela_lateral: # rename state of "window near the A/C" open >> aberta, closed >> fechada
        value_template: '{% if is_state("sensor.broadlink_s1c_janela_ac", "open") %}Aberta{% else %}Fechada{% endif %}'
        friendly_name: 'Janela lateral'

Like @AhmadK said

When I need a binary state changing to use particular vocabulary that is not supported by standard usage case option. I normally have to create a template sensor which also allows me to configure icons for the states too. e.g. : -

sensor:
  - platform: template
    sensors:
      s_occupancy_ladyinresidence:
        friendly_name: Lady Status
        entity_id: binary_sensor.bs_occupancy_lady
        value_template: "{% if is_state('binary_sensor.bs_occupancy_lady', 'on') %}In Residence{% else %}She's Out, It's Party Time !{% endif %}"
        icon_template: "{% if is_state('binary_sensor.bs_occupancy_lady', 'on') %}mdi:face-woman{% else %}mdi:party-popper{% endif %}"

This was a solution (adapted) for a guy who needed his window states reported in Portuguese

it’s a good solution but the main difference is the TS uses sensor, not binary_sensor and it makes his life a bit more complicated.

I believe I said that and yes, it does introduce ‘another’ entity but if you select another route, is that any better or ‘simpler’ ?
So far there’s been a lot of talking and no solutions.

I’m just pointing out that the TS won’t be able to use your solution directly and to be able to adapt it to his circumstances he will need to understand some basic concepts he apparently does not atm. There is nothing wrong with it.
Is that ‘simpler’ an irony? :wink: No, there are many routes and each one has its advantages and use cases so there is no single and ultimate solution I think.

I personally believe that in normal circumstances it’s better to teach a person how to fish rather than give them a fish. And the idea behind my ‘talking’ was to give TS some hints/links to important information on the subject and let him apply it and learn as he does that.
The trouble is some people take it very personal or consider it as ‘teaching’/‘preaching’ etc… I can do nothing about it, my intention here is to share my experience/help and learn in the same time, nothing else.

Thank you all ,it is solved now. I was adding to lovelace the wrong entities :worried:

My code is now like this:

sensor:
  - platform: broadlink # RM Pro Temperature
    scan_interval: 300
    host: 192.168.0.42
    mac: 'B4:43:0D:38:BB:1C'
    monitored_conditions:
      - 'temperature'
  - platform: systemmonitor # raspberry
    resources:
      - type: memory_use_percent
      - type: processor_use
      - type: disk_free
        arg: "/"
      - type: disk_use
        arg: "/"
      - type: network_in
        arg: eth0
      - type: network_out
        arg: eth0  
  - platform: template
    sensors:
      janela_principal:
        friendly_name: Janela Principal
        entity_id: sensor.broadlink_s1c_janela_principal
        value_template: "{% if is_state('sensor.broadlink_s1c_janela_principal', 'open') %}Aberta{% else %}Fechada{% endif %}"
        icon_template: "{% if is_state('sensor.broadlink_s1c_janela_principal', 'open') %}mdi:window-open{% else %}mdi:window-closed{% endif %}"
      janela_lateral:
        friendly_name: Janela Lateral
        entity_id: sensor.broadlink_s1c_janela_ac
        value_template: "{% if is_state('sensor.broadlink_s1c_janela_ac', 'open') %}Aberta{% else %}Fechada{% endif %}"
        icon_template: "{% if is_state('sensor.broadlink_s1c_janela_ac', 'open') %}mdi:window-open{% else %}mdi:window-closed{% endif %}"

  - platform: broadlink_s1c # Broadlink S1c, only using 2 door (window) sensors
    ip_address: 192.168.0.43
    mac: "34:EA:34:B6:6F:BF"
    timeout: 10

I was adding the entities “sensor.broadlink_s1c_janela_principal” and “sensor.broadlink_s1c_janela_ac” These are the entities that the system created.

Once I created the sensor template, now i have to add the entities “sensor.janela_principal” and “sensor.janela_lateral” … I can stil combine all 4 entities, 2 of them will be original, with “open” and “closed” and 2 with my custom states

Check this out:
all 4 windows combined, with right and wrong labels: (ignore the icons)
janelas all
lovelace:
janelas code

4 Likes

reboot your hass if you change something in the configuration.yaml file

Could you please tell me why my Air Quality sensors (A1) are showing numbers instead of the actual state of air, light and noise? They show 1, 2, 3 or 4…

What integration is that?

From the Broadlink integration, for the A1 sensor.
File named sensorfork.py

"Support for sensors."
from .device import device
from .exceptions import check_error


class a1(device):
    """Controls a Broadlink A1."""

    _SENSORS_AND_LEVELS = (
        ('light', ('dark', 'dim', 'normal', 'bright')),
        ('air_quality', ('excellent', 'good', 'normal', 'bad')),
        ('noise', ('quiet', 'normal', 'noisy')),
    )

    def __init__(self, *args, **kwargs) -> None:
        """Initialize the controller."""
        device.__init__(self, *args, **kwargs)
        self.type = "A1"

    def check_sensors(self) -> dict:
        """Return the state of the sensors."""
        data = self.check_sensors_raw()
        for sensor, levels in self._SENSORS_AND_LEVELS:
            try:
                data[sensor] = levels[data[sensor]]
            except IndexError:
                data[sensor] = 'unknown'
        return data

    def check_sensors_raw(self) -> dict:
        """Return the state of the sensors in raw format."""
        packet = bytearray([0x1])
        response = self.send_packet(0x6a, packet)
        check_error(response[0x22:0x24])
        payload = self.decrypt(response[0x38:])
        data = bytearray(payload[0x4:])
        return {
            'temperature': data[0x0] + data[0x1] / 10.0,
            'humidity': data[0x2] + data[0x3] / 10.0,
            'light': data[0x4],
            'air_quality': data[0x6],
            'noise': data[0x8],
        }

Thank you!

because it’s not translating the state using self._SENSORS_AND_LEVELS. the returned value is returing the bytearray from the payload.

SENSORS_AND_LEVELS = {
    'light': ('dark', 'dim', 'normal', 'bright'),
    'air_quality': ('excellent', 'good', 'normal', 'bad'),
    'noise': ('quiet', 'normal', 'noisy'),
}

class a1(device):
    """Controls a Broadlink A1."""

    def __init__(self, *args, **kwargs) -> None:
        """Initialize the controller."""
        device.__init__(self, *args, **kwargs)
        self.type = "A1"

    def check_sensors(self) -> dict:
        """Return the state of the sensors in raw format."""
        packet = bytearray([0x1])
        response = self.send_packet(0x6a, packet)
        check_error(response[0x22:0x24])
        payload = self.decrypt(response[0x38:])
        data = bytearray(payload[0x4:])
        return {
            'temperature': data[0x0] + data[0x1] / 10.0,
            'humidity': data[0x2] + data[0x3] / 10.0,
            'light': self.get_value('light', data[0x4]),
            'air_quality': self.get_value('air_quality', data[0x6]),
            'noise': self.get_value('noise', data[0x8]),
        }
    
    def get_value(self, item, index):
        try:
           return SENSORS_AND_LEVELS.get(item)[index]
        except IndexError:
           return "unknown"

You could also do this as well:

SENSORS_AND_LEVELS = {
    'light': {0: 'dark', 1: 'dim', 2: 'normal', 3: 'bright'},
    'air_quality': {0: 'excellent', 1: 'good', 2: 'normal',3: 'bad'},
    'noise': {0: 'quiet', 1: 'normal', 2: 'noisy'},
}
UNKNOWN = 'unknown'

class a1(device):
    """Controls a Broadlink A1."""

    def __init__(self, *args, **kwargs) -> None:
        """Initialize the controller."""
        device.__init__(self, *args, **kwargs)
        self.type = "A1"

    def check_sensors(self) -> dict:
        """Return the state of the sensors in raw format."""
        packet = bytearray([0x1])
        response = self.send_packet(0x6a, packet)
        check_error(response[0x22:0x24])
        payload = self.decrypt(response[0x38:])
        data = bytearray(payload[0x4:])
        return {
            'temperature': data[0x0] + data[0x1] / 10.0,
            'humidity': data[0x2] + data[0x3] / 10.0,
            'light': SENSORS_AND_LEVELS.get('light').get(data[0x4], UNKNOWN),
            'air_quality': SENSORS_AND_LEVELS.get('air_quality').get(data[0x6], UNKNOWN),
            'noise': SENSORS_AND_LEVELS.get('noise').get(data[0x8], UNKNOWN),
        }
    

I replaced the original code with the one you posted but still 1, 2, 3, 4… :frowning: