"""This script adds MQTT discovery support for Shellies devices.""" VERSION = "5.0.0" ATTR_ICON = "icon" ATTR_MANUFACTURER = "Allterco Robotics" ATTR_POWER_AC = "ac" ATTR_RELAY = "relay" ATTR_ROLLER = "roller" ATTR_SHELLY = "Shelly" ATTR_AVAILABILITY_EXTRA = "availability_extra" BUTTON_MUTE = "mute" BUTTON_RESTART = "restart" BUTTON_SELF_TEST = "self_test" BUTTON_UNMUTE = "unmute" BUTTON_VALVE_CLOSE = "valve_close" BUTTON_VALVE_OPEN = "valve_open" COMP_FAN = "fan" COMP_LIGHT = "light" COMP_SWITCH = "switch" CONF_DEFAULT_HEAT_TEMP = "default_heating_temperature" CONF_DEVELOP = "develop" CONF_DEVICE_NAME = "device_name" CONF_DISCOVERY_PREFIX = "discovery_prefix" CONF_EXPIRE_AFTER = "expire_after" CONF_EXT_SWITCH = "ext-switch" CONF_FORCE_UPDATE_SENSORS = "force_update_sensors" CONF_FRIENDLY_NAME = "friendly_name" CONF_FW_VER = "fw_ver" CONF_HOST = "host" CONF_ID = "id" CONF_IGNORE_DEVICE_MODEL = "ignore_device_model" CONF_IGNORED_DEVICES = "ignored_devices" CONF_MAC = "mac" CONF_MINIMAL_VALVE_POSITION = "minimal_valve_position" CONF_MODE = "mode" CONF_MODEL_ID = "model" CONF_OPTIMISTIC = "optimistic" CONF_POSITION_TEMPLATE = "position_template" CONF_POWERED = "powered" CONF_QOS = "qos" CONF_SET_POSITION_TEMPLATE = "set_position_template" CONF_USE_FAHRENHEIT = "use_fahrenheit" CONF_VALVE_CONNECTED = "valve_connected" DEFAULT_DISC_PREFIX = "homeassistant" DEVICE_CLASS_AWNING = "awning" DEVICE_CLASS_BATTERY = "battery" DEVICE_CLASS_BATTERY_CHARGING = "battery_charging" DEVICE_CLASS_BLIND = "blind" DEVICE_CLASS_BUTTON = "button" DEVICE_CLASS_COLD = "cold" DEVICE_CLASS_CONNECTIVITY = "connectivity" DEVICE_CLASS_CURRENT = "current" DEVICE_CLASS_CURTAIN = "curtain" DEVICE_CLASS_DAMPER = "damper" DEVICE_CLASS_DOOR = "door" DEVICE_CLASS_ENERGY = "energy" DEVICE_CLASS_ENUM = "enum" DEVICE_CLASS_FIRMWARE = "firmware" DEVICE_CLASS_GARAGE = "garage" DEVICE_CLASS_GARAGE_DOOR = "garage_door" DEVICE_CLASS_GAS = "gas" DEVICE_CLASS_GATE = "gate" DEVICE_CLASS_HEAT = "heat" DEVICE_CLASS_HUMIDITY = "humidity" DEVICE_CLASS_ILLUMINANCE = "illuminance" DEVICE_CLASS_LIGHT = "light" DEVICE_CLASS_LOCK = "lock" DEVICE_CLASS_MOISTURE = "moisture" DEVICE_CLASS_MOTION = "motion" DEVICE_CLASS_MOVING = "moving" DEVICE_CLASS_OCCUPANCY = "occupancy" DEVICE_CLASS_OPENING = "opening" DEVICE_CLASS_PLUG = "plug" DEVICE_CLASS_POWER = "power" DEVICE_CLASS_POWER_FACTOR = "power_factor" DEVICE_CLASS_PRESENCE = "presence" DEVICE_CLASS_PRESSURE = "pressure" DEVICE_CLASS_PROBLEM = "problem" DEVICE_CLASS_RESTART = "restart" DEVICE_CLASS_SAFETY = "safety" DEVICE_CLASS_SHADE = "shade" DEVICE_CLASS_SHUTTER = "shutter" DEVICE_CLASS_SIGNAL_STRENGTH = "signal_strength" DEVICE_CLASS_SMOKE = "smoke" DEVICE_CLASS_SOUND = "sound" DEVICE_CLASS_TEMPERATURE = "temperature" DEVICE_CLASS_TIMESTAMP = "timestamp" DEVICE_CLASS_UPDATE = "update" DEVICE_CLASS_VIBRATION = "vibration" DEVICE_CLASS_VOLTAGE = "voltage" DEVICE_CLASS_WINDOW = "window" ENTITY_CATEGORY_CONFIG = "config" ENTITY_CATEGORY_DIAGNOSTIC = "diagnostic" EXPIRE_AFTER_FOR_BATTERY_POWERED = int(1.2 * 12 * 60 * 60) # 1.2 * 12 h EXPIRE_AFTER_FOR_AC_POWERED = int(2.2 * 10 * 60) # 2.2 * 10 min EXPIRE_AFTER_FOR_SHELLY_MOTION = int(1.2 * 60 * 60) # 1.2 * 60 min EXPIRE_AFTER_FOR_SHELLY_VALVE = int(1.2 * 60 * 60) # 1.2 * 60 min KEY_ACTION_TEMPLATE = "act_tpl" KEY_ACTION_TOPIC = "act_t" KEY_AUTOMATION_TYPE = "atype" KEY_AVAILABILITY = "avty" KEY_BRIGHTNESS_COMMAND_TEMPLATE = "bri_cmd_tpl" KEY_BRIGHTNESS_COMMAND_TOPIC = "bri_cmd_t" KEY_BRIGHTNESS_STATE_TOPIC = "bri_stat_t" KEY_BRIGHTNESS_TEMPLATE = "bri_tpl" KEY_BRIGHTNESS_VALUE_TEMPLATE = "bri_val_tpl" KEY_COLOR_TEMP_TEMPLATE = "clr_temp_tpl" KEY_COMMAND_OFF_TEMPLATE = "cmd_off_tpl" KEY_COMMAND_ON_TEMPLATE = "cmd_on_tpl" KEY_COMMAND_TEMPLATE = "cmd_tpl" KEY_COMMAND_TOPIC = "cmd_t" KEY_CONFIGURATION_URL = "cu" KEY_CONNECTIONS = "cns" KEY_CURRENT_TEMPERATURE_TEMPLATE = "curr_temp_tpl" KEY_CURRENT_TEMPERATURE_TOPIC = "curr_temp_t" KEY_DEVICE = "dev" KEY_DEVICE_CLASS = "dev_cla" KEY_EFFECT_COMMAND_TEMPLATE = "fx_cmd_tpl" KEY_EFFECT_COMMAND_TOPIC = "fx_cmd_t" KEY_EFFECT_LIST = "fx_list" KEY_EFFECT_STATE_TOPIC = "fx_stat_t" KEY_EFFECT_VALUE_TEMPLATE = "fx_val_tpl" KEY_ENABLED_BY_DEFAULT = "en" KEY_ENTITY_CATEGORY = "ent_cat" KEY_ENTITY_PICTURE = "ent_pic" KEY_EVENT_TYPES = "evt_typ" KEY_EXPIRE_AFTER = "exp_aft" KEY_FORCE_UPDATE = "frc_upd" KEY_HW_VERSION = "hw" KEY_ICON = "icon" KEY_JSON_ATTRIBUTES_TEMPLATE = "json_attr_tpl" KEY_JSON_ATTRIBUTES_TOPIC = "json_attr_t" KEY_LATEST_VERSION_TEMPLATE = "l_ver_tpl" KEY_LATEST_VERSION_TOPIC = "l_ver_t" KEY_MAC = "mac" KEY_MANUFACTURER = "mf" KEY_MAX = "max" KEY_MAX_MIREDS = "max_mirs" KEY_MAX_TEMP = "max_temp" KEY_MIN = "min" KEY_MIN_MIREDS = "min_mirs" KEY_MIN_TEMP = "min_temp" KEY_MODE_COMMAND_TEMPLATE = "mode_cmd_tpl" KEY_MODE_COMMAND_TOPIC = "mode_cmd_t" KEY_MODE_STATE_TEMPLATE = "mode_stat_tpl" KEY_MODE_STATE_TOPIC = "mode_stat_t" KEY_MODEL = "mdl" KEY_MODES = "modes" KEY_NAME = "name" KEY_OFF_DELAY = "off_dly" KEY_OPTIMISTIC = "opt" KEY_OPTIONS = "options" KEY_ORIGIN = "o" KEY_PAYLOAD = "pl" KEY_PAYLOAD_AVAILABLE = "pl_avail" KEY_PAYLOAD_CLOSE = "pl_cls" KEY_PAYLOAD_INSTALL = "pl_inst" KEY_PAYLOAD_NOT_AVAILABLE = "pl_not_avail" KEY_PAYLOAD_OFF = "pl_off" KEY_PAYLOAD_ON = "pl_on" KEY_PAYLOAD_OPEN = "pl_open" KEY_PAYLOAD_PRESS = "pl_prs" KEY_PAYLOAD_STOP = "pl_stop" KEY_POSITION_TEMPLATE = "pos_tpl" KEY_POSITION_TOPIC = "pos_t" KEY_PRECISION = "precision" KEY_QOS = "qos" KEY_RELEASE_URL = "rel_u" KEY_REPORTS_POSITION = "pos" KEY_RETAIN = "ret" KEY_RGBW_COMMAND_TEMPLATE = "rgbw_cmd_tpl" KEY_RGBW_COMMAND_TOPIC = "rgbw_cmd_t" KEY_RGBW_STATE_TOPIC = "rgbw_stat_t" KEY_RGBW_VALUE_TEMPLATE = "rgbw_val_tpl" KEY_SCHEMA = "schema" KEY_SET_POSITION_TEMPLATE = "set_pos_tpl" KEY_SET_POSITION_TOPIC = "set_pos_t" KEY_STATE_CLASS = "stat_cla" KEY_STATE_CLOSING = "stat_closing" KEY_STATE_OFF = "stat_off" KEY_STATE_ON = "stat_on" KEY_STATE_OPENING = "stat_opening" KEY_STATE_STOPPED = "stat_stopped" KEY_STATE_TEMPLATE = "stat_tpl" KEY_STATE_TOPIC = "stat_t" KEY_STATE_VALUE_TEMPLATE = "stat_val_tpl" KEY_STEP = "step" KEY_SUBTYPE = "stype" KEY_SUGGESTED_DISPLAY_PRECISION = "sug_dsp_prc" KEY_SUPPORT_URL = "url" KEY_SW_VERSION = "sw" KEY_TEMP_STEP = "temp_step" KEY_TEMPERATURE_COMMAND_TEMPLATE = "temp_cmd_tpl" KEY_TEMPERATURE_COMMAND_TOPIC = "temp_cmd_t" KEY_TEMPERATURE_STATE_TEMPLATE = "temp_stat_tpl" KEY_TEMPERATURE_STATE_TOPIC = "temp_stat_t" KEY_TITLE = "tit" KEY_TOPIC = "t" KEY_TYPE = "type" KEY_UNIQUE_ID = "uniq_id" KEY_UNIT = "unit_of_meas" KEY_VALUE_TEMPLATE = "val_tpl" LIGHT_COLOR = "color" LIGHT_WHITE = "white" # Maximum light transition time in milliseconds MAX_TRANSITION = 5000 # Firmware 1.6.5 release date MIN_4PRO_FIRMWARE_DATE = 20200408 # Firmware 1.9.4 release date MIN_PLUG_FIRMWARE_DATE = 20210115 # Firmware 1.1.0 release date MIN_MOTION_FIRMWARE_DATE = 20220517 # Firmware 2.1.4-rc1 release date MIN_MOTION2_FIRMWARE_DATE = 20220301 # Firmware 2.2.1 release date MIN_VALVE_FIRMWARE_DATE = 20231009 # Firmware 1.11.7 release date MIN_DIMMER_FIRMWARE_DATE = 20211109 # Firmware 1.11.7 release date MIN_HT_FIRMWARE_DATE = 20211109 # Firmware 1.11.8 release date MIN_FIRMWARE_DATE = 20220209 MAX_MQTT_TOPIC_LENGTH = 32 MODEL_SHELLY1 = f"{ATTR_SHELLY} 1" MODEL_SHELLY1L = f"{ATTR_SHELLY} 1L" MODEL_SHELLY1PM = f"{ATTR_SHELLY} 1PM" MODEL_SHELLY2 = f"{ATTR_SHELLY} 2" MODEL_SHELLY25 = f"{ATTR_SHELLY} 2.5" MODEL_SHELLY3EM = f"{ATTR_SHELLY} 3EM" MODEL_SHELLY4PRO = f"{ATTR_SHELLY} 4Pro" MODEL_SHELLYAIR = f"{ATTR_SHELLY} Air" MODEL_SHELLYBUTTON1 = f"{ATTR_SHELLY} Button1" MODEL_SHELLYDIMMER = f"{ATTR_SHELLY} Dimmer" MODEL_SHELLYDIMMER2 = f"{ATTR_SHELLY} Dimmer 2" MODEL_SHELLYDUO = f"{ATTR_SHELLY} DUO" MODEL_SHELLYDW = f"{ATTR_SHELLY} Door/Window" MODEL_SHELLYDW2 = f"{ATTR_SHELLY} Door/Window 2" MODEL_SHELLYEM = f"{ATTR_SHELLY} EM" MODEL_SHELLYFLOOD = f"{ATTR_SHELLY} Flood" MODEL_SHELLYGAS = f"{ATTR_SHELLY} Gas" MODEL_SHELLYHT = f"{ATTR_SHELLY} H&T" MODEL_SHELLYI3 = f"{ATTR_SHELLY} i3" MODEL_SHELLYMOTION = f"{ATTR_SHELLY} Motion" MODEL_SHELLYMOTION2 = f"{ATTR_SHELLY} Motion 2" MODEL_SHELLYPLUG = f"{ATTR_SHELLY} Plug" MODEL_SHELLYPLUG_S = f"{ATTR_SHELLY} Plug S" MODEL_SHELLYPLUG_US = f"{ATTR_SHELLY} Plug US" MODEL_SHELLYRGBW2 = f"{ATTR_SHELLY} RGBW2" MODEL_SHELLYSENSE = f"{ATTR_SHELLY} Sense" MODEL_SHELLYSMOKE = f"{ATTR_SHELLY} Smoke" MODEL_SHELLYUNI = f"{ATTR_SHELLY} UNI" MODEL_SHELLYVALVE = f"{ATTR_SHELLY} Valve" MODEL_SHELLYVINTAGE = f"{ATTR_SHELLY} Vintage" MODEL_SHELLY1_ID = "SHSW-1" # Shelly 1 MODEL_SHELLY1_PREFIX = "shelly1" MODEL_SHELLY1L_ID = "SHSW-L" # Shelly 1L MODEL_SHELLY1L_PREFIX = "shelly1l" MODEL_SHELLY1PM_ID = "SHSW-PM" # Shelly 1PM MODEL_SHELLY1PM_PREFIX = "shelly1pm" MODEL_SHELLY2_ID = "SHSW-21" # Shelly 2 MODEL_SHELLY2_PREFIX = "shellyswitch" MODEL_SHELLY25_ID = "SHSW-25" # Shelly 2.5 MODEL_SHELLY25_PREFIX = "shellyswitch25" MODEL_SHELLY3EM_ID = "SHEM-3" # Shelly 3EM MODEL_SHELLY3EM_PREFIX = "shellyem3" MODEL_SHELLY4PRO_ID = "SHSW-44" # Shelly 4Pro MODEL_SHELLY4PRO_PREFIX = "shelly4pro" MODEL_SHELLYAIR_ID = "SHAIR-1" # Shelly Air MODEL_SHELLYAIR_PREFIX = "shellyair" MODEL_SHELLYBUTTON1_ID = "SHBTN-1" # Shelly Button1 MODEL_SHELLYBUTTON1V2_ID = "SHBTN-2" # Shelly Button1 v2 MODEL_SHELLYBUTTON1_PREFIX = "shellybutton1" MODEL_SHELLYDIMMER_ID = "SHDM-1" # Shelly Dimmer MODEL_SHELLYDIMMER_PREFIX = "shellydimmer" MODEL_SHELLYDIMMER2_ID = "SHDM-2" # Shelly Dimmer 2 MODEL_SHELLYDIMMER2_PREFIX = "shellydimmer2" MODEL_SHELLYDUO_ID = "SHBDUO-1" # Shelly Duo MODEL_SHELLYDUO_PREFIX = "shellybulbduo" MODEL_SHELLYDW_ID = "SHDW-1" # Shelly Door/Window MODEL_SHELLYDW_PREFIX = "shellydw" MODEL_SHELLYDW2_ID = "SHDW-2" # Shelly Door/Window 2 MODEL_SHELLYDW2_PREFIX = "shellydw2" MODEL_SHELLYEM_ID = "SHEM" # Shelly EM MODEL_SHELLYEM_PREFIX = "shellyem" MODEL_SHELLYFLOOD_ID = "SHWT-1" # Shelly Flood MODEL_SHELLYFLOOD_PREFIX = "shellyflood" MODEL_SHELLYGAS_ID = "SHGS-1" # Shelly Gas MODEL_SHELLYGAS_PREFIX = "shellygas" MODEL_SHELLYHT_ID = "SHHT-1" # Shelly H&T MODEL_SHELLYHT_PREFIX = "shellyht" MODEL_SHELLYI3_ID = "SHIX3-1" # Shelly i3 MODEL_SHELLYI3_PREFIX = "shellyix3" MODEL_SHELLYMOTION_ID = "SHMOS-01" # Shelly Motion MODEL_SHELLYMOTION_PREFIX = "shellymotionsensor" MODEL_SHELLYMOTION2_ID = "SHMOS-02" # Shelly Motion MODEL_SHELLYMOTION2_PREFIX = "shellymotionsensor2" MODEL_SHELLYPLUG_ID = "SHPLG-1" # Shelly Plug MODEL_SHELLYPLUG_E_ID = "SHPLG2-1" # Shelly Plug E MODEL_SHELLYPLUG_PREFIX = "shellyplug" MODEL_SHELLYPLUG_S_ID = "SHPLG-S" # Shelly Plug S MODEL_SHELLYPLUG_S_PREFIX = "shellyplug-s" MODEL_SHELLYPLUG_US_ID = "SHPLG-U1" # Shelly Plug US MODEL_SHELLYPLUG_US_PREFIX = "shellyplug-u1" MODEL_SHELLYRGBW2_ID = "SHRGBW2" # Shelly RGBW2 MODEL_SHELLYRGBW2_PREFIX = "shellyrgbw2" MODEL_SHELLYSENSE_ID = "SHSEN-1" # Shelly Sense MODEL_SHELLYSENSE_PREFIX = "shellysense" MODEL_SHELLYSMOKE_ID = "SHSM-01" # Shelly Smoke MODEL_SHELLYSMOKE_PREFIX = "shellysmoke" MODEL_SHELLYVALVE_ID = "SHTRV-01" # Shelly Valve MODEL_SHELLYVALVE_PREFIX = "shellytrv" MODEL_SHELLYVINTAGE_ID = "SHVIN-1" # Shelly Vintage MODEL_SHELLYVINTAGE_PREFIX = "shellyvintage" MODEL_SHELLYUNI_ID = "SHUNI-1" # Shelly UNI MODEL_SHELLYUNI_PREFIX = "shellyuni" NUMBER_BOOST_TIME = "boost_time" NUMBER_MINIMAL_VALVE_POSITION = "minimal_valve_position" NUMBER_VALVE_POSITION = "valve_position" NUMBER_LIGHT_BRIGHTNESS = "brightness" OFF_DELAY = 1 PL_CLOSE = "close" PL_MUTE = "mute" PL_OPEN = "open" PL_RESTART = "reboot" PL_SELF_TEST = "self_test" PL_UNMUTE = "unmute" PL_UPDATE_FIRMWARE = "update_fw" SELECT_PROFILES = "profiles" SENSOR_ADC = "adc" SENSOR_AUTOMATIC_TEMPERATURE_CONTROL = "automatic_temperature_control" SENSOR_BATTERY = "battery" SENSOR_CALIBRATED = "calibrated" SENSOR_CHARGER = "charger" SENSOR_CLOUD = "cloud" SENSOR_CONCENTRATION = "concentration" SENSOR_CURRENT = "current" SENSOR_ENERGY = "energy" SENSOR_EXT_HUMIDITY = "ext_humidity" SENSOR_EXT_SWITCH = "ext_switch" SENSOR_EXT_TEMPERATURE = "ext_temperature" SENSOR_FLOOD = "flood" SENSOR_GAS = "gas" SENSOR_HUMIDITY = "humidity" SENSOR_ILLUMINATION = "illumination" SENSOR_INPUT_0 = "input 0" SENSOR_INPUT_1 = "input 1" SENSOR_INPUT_2 = "input 2" SENSOR_INPUT_3 = "input 3" SENSOR_IP = "ip" SENSOR_IX_SUM_CURRENT = "ix_sum_current" SENSOR_LAST_RESTART = "last_restart" SENSOR_LOADERROR = "loaderror" SENSOR_LUX = "lux" SENSOR_MOTION = "motion" SENSOR_N_CURRENT = "n_current" SENSOR_OPENING = "opening" SENSOR_OPERATION = "operation" SENSOR_OVERLOAD = "overload" SENSOR_OVERPOWER = "overpower" SENSOR_OVERPOWER_VALUE = "overpower_value" SENSOR_OVERTEMPERATURE = "overtemperature" SENSOR_POWER = "power" SENSOR_POWER_FACTOR = "pf" SENSOR_REACTIVE_POWER = "reactive_power" SENSOR_REPORTED_WINDOW_STATE = "reported_window_state" SENSOR_RETURNED_ENERGY = "returned_energy" SENSOR_RSSI = "rssi" SENSOR_SELF_TEST = "self_test" SENSOR_SMOKE = "smoke" SENSOR_SSID = "ssid" SENSOR_TEMPERATURE = "temperature" SENSOR_TEMPERATURE_F = "temperature_f" SENSOR_TEMPERATURE_STATUS = "temperature_status" SENSOR_TILT = "tilt" SENSOR_TOTAL = "total" SENSOR_TOTAL_RETURNED = "total_returned" SENSOR_TOTALWORKTIME = "totalworktime" SENSOR_UPTIME = "uptime" SENSOR_VALVE = "valve" SENSOR_VIBRATION = "vibration" SENSOR_VOLTAGE = "voltage" SENSOR_WINDOW_STATE_REPORTING = "window_state_reporting" UPDATE_FIRMWARE = "firmware" VALVE_GAS = "gas" STATE_CLASS_MEASUREMENT = "measurement" STATE_CLASS_TOTAL_INCREASING = "total_increasing" SWITCH_ACCELERATED_HEATING = "accelerated_heating" SWITCH_SCHEDULE = "schedule" TOPIC_ADC = "~adc/0" TOPIC_ANNOUNCE = "~announce" TOPIC_CHARGER = "~charger" TOPIC_COLOR_0_STATUS = "~color/0/status" TOPIC_COMMAND = "~command" TOPIC_COMMAND_ACCELERATED_HEATING = "~thermostat/0/command/accelerated_heating" TOPIC_COMMAND_BOOST_MINUTES = "~thermostat/0/command/boost_minutes" TOPIC_COMMAND_PROFILES = "~thermostat/0/command/schedule_profile" TOPIC_COMMAND_SCHEDULE = "~thermostat/0/command/schedule" TOPIC_COMMAND_VALVE_MIN = "~thermostat/0/command/valve_min_percent" TOPIC_COMMAND_VALVE_POSITION = "~thermostat/0/command/valve_pos" TOPIC_ENERGY = "~relay/energy" TOPIC_EXT_SWITCH = "~ext_switch/0" TOPIC_INFO = "~info" TOPIC_INPUT_0 = "~input/0" TOPIC_INPUT_1 = "~input/1" TOPIC_INPUT_2 = "~input/2" TOPIC_INPUT_3 = "~input/3" TOPIC_IX_SUM_CURRENT = "~emeter_n/ixsum" TOPIC_LIGHT_ENERGY = "~light/{light_id}/energy" TOPIC_LIGHT_ENERGY_RGBW2_COLOR = "~color/{light_id}/energy" TOPIC_LIGHT_ENERGY_RGBW2_WHITE = "~white/{light_id}/energy" TOPIC_LIGHT_OVERPOWER_VALUE = "~light/{light_id}/overpower_value" TOPIC_LIGHT_POWER = "~light/{light_id}/power" TOPIC_LIGHT_POWER_RGBW2_COLOR = "~color/{light_id}/power" TOPIC_LIGHT_POWER_RGBW2_WHITE = "~white/{light_id}/power" TOPIC_LIGHT_SET = "~light/{light_id}/set" TOPIC_LIGHT_STATUS = "~light/{light_id}/status" TOPIC_LOADERROR = "~loaderror" TOPIC_METER_CURRENT = "~emeter/{meter_id}/current" TOPIC_METER_ENERGY = "~emeter/{meter_id}/energy" TOPIC_METER_POWER = "~emeter/{meter_id}/power" TOPIC_METER_POWER_FACTOR = "~emeter/{meter_id}/pf" TOPIC_METER_REACTIVE_POWER = "~emeter/{meter_id}/reactive_power" TOPIC_METER_RETURNED_ENERGY = "~emeter/{meter_id}/returned_energy" TOPIC_METER_TOTAL = "~emeter/{meter_id}/total" TOPIC_METER_TOTAL_RETURNED = "~emeter/{meter_id}/total_returned" TOPIC_METER_VOLTAGE = "~emeter/{meter_id}/voltage" TOPIC_MUTE = "~sensor/mute" TOPIC_N_CURRENT = "~emeter_n/current" TOPIC_ONLINE = "~online" TOPIC_OVERLOAD = "~overload" TOPIC_OVERPOWER = "~overpower" TOPIC_OVERPOWER_VALUE = "overpower_value" TOPIC_OVERTEMPERATURE = "~overtemperature" TOPIC_POWER = "~relay/power" TOPIC_RELAY = "~relay/{relay_id}" TOPIC_RELAY_ENERGY = "~relay/{relay_id}/energy" TOPIC_RELAY_POWER = "~relay/{relay_id}/power" TOPIC_ROLLER_ENERGY = "~roller/0/energy" TOPIC_ROLLER_POWER = "~roller/0/power" TOPIC_SELF_TEST = "~sensor/start_self_test" TOPIC_SENSOR_BATTERY = "~sensor/battery" TOPIC_SENSOR_CHARGER = "~sensor/charger" TOPIC_SENSOR_CONCENTRATION = "~sensor/concentration" TOPIC_SENSOR_GAS = "~sensor/gas" TOPIC_SENSOR_HUMIDITY = "~sensor/humidity" TOPIC_SENSOR_LUX = "~sensor/lux" TOPIC_SENSOR_OPERATION = "~sensor/operation" TOPIC_SENSOR_SELF_TEST = "~sensor/self_test" TOPIC_SENSOR_SMOKE = "~sensor/smoke" TOPIC_SENSOR_STATE = "~sensor/state" TOPIC_SENSOR_FLOOD = "~sensor/flood" TOPIC_SENSOR_TEMPERATURE = "~sensor/temperature" TOPIC_SENSOR_TILT = "~sensor/tilt" TOPIC_SENSOR_UNMUTE = "~sensor/unmute" TOPIC_SENSOR_VIBRATION = "~sensor/vibration" TOPIC_SETTINGS = "~settings" TOPIC_STATUS = "~status" TOPIC_TEMPERATURE = "~temperature" TOPIC_TEMPERATURE_STATUS = "~temperature_status" TOPIC_TOTAL_WORK_TIME = "~totalworktime" TOPIC_VALVE = "~valve/0/state" TOPIC_VALVE_COMMAND = "~valve/0/command" TOPIC_VOLTAGE = "~voltage" TOPIC_WHITE_SET = "~white/{light_id}/set" TOPIC_WHITE_STATUS = "~white/{light_id}/status" TPL_ACCELERATED_HEATING = "{{value_json.thermostats.0.target_t.accelerated_heating}}" TPL_ACTION_TEMPLATE = "{{%if value_json.thermostats.0.target_t.value<={min_temp}%}}off{{%elif value_json.thermostats.0.pos=={min_pos}%}}idle{{%else%}}heating{{%endif%}}" TPL_AUTOMATIC_TEMPERATURE_CONTROL = ( "{%if value_json.target_t.enabled%}ON{%else%}OFF{%endif%}" ) TPL_BATTERY_FROM_INFO = "{{value_json.bat.value}}" TPL_BATTERY_FROM_JSON = "{{value_json.bat}}" TPL_BOOST_MINUTES = "{{value_json.thermostats.0.boost_minutes}}" TPL_CALIBRATED = "{%if value_json.calibrated%}ON{%else%}OFF{%endif%}" TPL_CALIBRATED_AVAILABILITY = ( "{%if value_json.calibrated%}online{%else%}offline{%endif%}" ) TPL_CHARGER = "{%if value_json.charger%}ON{%else%}OFF{%endif%}" TPL_CLOUD = "{%if value_json.cloud.connected%}ON{%else%}OFF{%endif%}" TPL_COLOR_TEMP_WHITE_LIGHT = ( "{{((1000000/(value_json.temp|int,2700)|max)|round(0,^floor^))}}" ) TPL_COMMAND_ON_WHITE_LIGHT = "{{^turn^:^on^{{%if brightness is defined%}},^brightness^:{{{{brightness|float|multiply(0.3922)|round}}}}{{%endif%}}{{%if transition is defined%}},^transition^:{{{{min(transition|multiply(1000), {max_transition})}}}}{{%endif%}}}}" TPL_COMMAND_SET_BRIGHTNESS_WHITE_LIGHT = "{{^brightness^:{{{{value|float|round}}}}}}" TPL_COMMAND_ON_WHITE_LIGHT_DUO = "{{^turn^:^on^{{%if brightness is defined%}},^brightness^:{{{{brightness|float|multiply(0.3922)|round}}}}{{%endif%}}{{%if color_temp is defined%}},^temp^:{{{{(1000000/(color_temp|int))|round(0,^floor^)}}}}{{%endif%}}{{%if transition is defined%}},^transition^:{{{{min(transition|multiply(1000), {max_transition})}}}}{{%endif%}}}}" TPL_COMMAND_PROFILES = "{{value.split(^ ^)[-1]}}" TPL_CONCENTRATION = "{%if is_number(value) and 0<=value|int<=65535%}{{value}}{%endif%}" TPL_CURRENT_TEMPERATURE = "{{value_json.thermostats.0.tmp.value}}" TPL_ENERGY_WMIN = "{{value|float/60}}" TPL_EVENT = ( "{%if value_json.event%}{{{^event_type^:value_json.event}|to_json}}{%endif%}" ) TPL_GAS = "{%if value in [^mild^,^heavy^]%}ON{%else%}OFF{%endif%}" TPL_GAS_TO_JSON = "{{{^status^:value}|tojson}}" TPL_HUMIDITY = "{%if is_number(value) and 0 MAX_MQTT_TOPIC_LENGTH: raise ValueError( f"id value {dev_id} is longer than 32 characters, use shorter custom MQTT prefix" ) if not mac: raise ValueError(f"mac value {mac} is not valid, check script configuration") if not fw_ver: raise ValueError(f"fw_ver value {fw_ver} is not valid, check script configuration") mac = str(mac).lower() dev_id_prefix = dev_id.rsplit("-", 1)[0].lower() # compatibility with old firmware if dev_id_prefix == MODEL_SHELLY4PRO_PREFIX: model_id = MODEL_SHELLY4PRO_ID if not model_id: raise ValueError("model_id value None is not valid, check script configuration") try: cur_ver_date = parse_version(fw_ver) except (IndexError, ValueError) as exc: raise ValueError( f"Firmware version {fw_ver} is not supported, update your device {dev_id}" ) from exc min_ver_date = DEVICE_FIRMWARE_MAP.get(model_id, 0) if cur_ver_date < min_ver_date: raise ValueError( f"Firmware dated {min_ver_date} is required, update your device {dev_id}" ) logger.debug( # noqa: F821 "id: %s, mac: %s, fw_ver: %s, model: %s", dev_id, mac, fw_ver, model_id ) qos = data.get(CONF_QOS, 0) # noqa: F821 if qos not in (0, 1, 2): raise ValueError(f"QoS value {qos} is not valid, check script configuration") optimistic = data.get(CONF_OPTIMISTIC, False) # noqa: F821 if not isinstance(optimistic, bool): optimistic = False disc_prefix = data.get(CONF_DISCOVERY_PREFIX, DEFAULT_DISC_PREFIX) # noqa: F821 ignore_device_model = data.get(CONF_IGNORE_DEVICE_MODEL, False) # noqa: F821 if not isinstance(ignore_device_model, bool): ignore_device_model = False develop = data.get(CONF_DEVELOP, False) # noqa: F821 if develop: disc_prefix = "develop" retain = False logger.error("DEVELOP MODE !!!") # noqa: F821 battery_powered = False buttons = {} climate_entity_option = {} ext_humi_sensors = 0 ext_temp_sensors = 0 inputs = 0 inputs_types = [] lights_bin_sensors = [] lights_bin_sensors_device_classes = [] lights_bin_sensors_pl = [] lights_bin_sensors_tpls = [] meters = 0 meter_sensors = {} model = None relay_components = [COMP_SWITCH, COMP_LIGHT, COMP_FAN] relays = 0 relay_binary_sensors = {} relay_sensors = {} light_binary_sensors = {} light_sensors = {} light_numbers = {} rgbw_lights = 0 rollers = 0 updates = {} binary_sensors = {} numbers = {} selectors = {} sensors = {} switches = {} valves = {} white_lights = {} if model_id == MODEL_SHELLY1_ID or dev_id_prefix == MODEL_SHELLY1_PREFIX: model = MODEL_SHELLY1 ext_humi_sensors = 1 ext_temp_sensors = 3 inputs = 1 relays = 1 binary_sensors = { SENSOR_EXT_SWITCH: OPTIONS_SENSOR_EXT_SWITCH, SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, } inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLY1L_ID or dev_id_prefix == MODEL_SHELLY1L_PREFIX: model = MODEL_SHELLY1L ext_humi_sensors = 1 ext_temp_sensors = 3 inputs = 2 relays = 1 binary_sensors = { SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_INPUT_1: OPTIONS_SENSOR_INPUT_1, SENSOR_OVERTEMPERATURE: OPTIONS_SENSOR_OVERTEMPERATURE, } inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] relay_sensors = { SENSOR_POWER: OPTIONS_SENSOR_RELAY_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_RELAY_ENERGY, } sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_TEMPERATURE: OPTIONS_SENSOR_DEVICE_TEMPERATURE, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLY1PM_ID or dev_id_prefix == MODEL_SHELLY1PM_PREFIX: model = MODEL_SHELLY1PM relays = 1 inputs = 1 ext_humi_sensors = 1 ext_temp_sensors = 3 inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] relay_sensors = { SENSOR_POWER: OPTIONS_SENSOR_RELAY_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_RELAY_ENERGY, } relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_TEMPERATURE: OPTIONS_SENSOR_DEVICE_TEMPERATURE, SENSOR_TEMPERATURE_STATUS: OPTIONS_SENSOR_TEMPERATURE_STATUS, } binary_sensors = { SENSOR_OVERTEMPERATURE: OPTIONS_SENSOR_OVERTEMPERATURE, SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} if model_id == MODEL_SHELLYAIR_ID or dev_id_prefix == MODEL_SHELLYAIR_PREFIX: model = MODEL_SHELLYAIR relays = 1 relay_sensors = { SENSOR_POWER: OPTIONS_SENSOR_RELAY_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_RELAY_ENERGY, } sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_TEMPERATURE: OPTIONS_SENSOR_DEVICE_TEMPERATURE, SENSOR_TOTALWORKTIME: OPTIONS_SENSOR_TOTALWORKTIME, } binary_sensors = { SENSOR_OVERTEMPERATURE: OPTIONS_SENSOR_OVERTEMPERATURE, SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, } ext_temp_sensors = 1 buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLY2_ID or dev_id_prefix == MODEL_SHELLY2_PREFIX: model = MODEL_SHELLY2 relays = 2 rollers = 1 inputs = 2 inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} binary_sensors = { SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_INPUT_1: OPTIONS_SENSOR_INPUT_1, } sensors = { SENSOR_ENERGY: OPTIONS_SENSOR_ENERGY, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_POWER: OPTIONS_SENSOR_POWER, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_VOLTAGE: OPTIONS_SENSOR_VOLTAGE, } if roller_mode: sensors[SENSOR_ENERGY] = OPTIONS_SENSOR_ROLLER_ENERGY sensors[SENSOR_POWER] = OPTIONS_SENSOR_ROLLER_POWER else: sensors[SENSOR_ENERGY] = OPTIONS_SENSOR_ENERGY sensors[SENSOR_POWER] = OPTIONS_SENSOR_POWER buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLY25_ID or dev_id_prefix == MODEL_SHELLY25_PREFIX: model = MODEL_SHELLY25 relays = 2 rollers = 1 inputs = 2 inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] relay_sensors = { SENSOR_POWER: OPTIONS_SENSOR_RELAY_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_RELAY_ENERGY, } relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_TEMPERATURE: OPTIONS_SENSOR_DEVICE_TEMPERATURE, SENSOR_TEMPERATURE_STATUS: OPTIONS_SENSOR_TEMPERATURE_STATUS, SENSOR_VOLTAGE: OPTIONS_SENSOR_VOLTAGE, SENSOR_ENERGY: OPTIONS_SENSOR_ROLLER_ENERGY, SENSOR_POWER: OPTIONS_SENSOR_ROLLER_POWER, } binary_sensors = { SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_INPUT_1: OPTIONS_SENSOR_INPUT_1, SENSOR_OVERTEMPERATURE: OPTIONS_SENSOR_OVERTEMPERATURE, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYUNI_ID or dev_id_prefix == MODEL_SHELLYUNI_PREFIX: model = MODEL_SHELLYUNI inputs = 2 relays = 2 ext_humi_sensors = 1 ext_temp_sensors = 3 inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} sensors = { SENSOR_ADC: OPTIONS_SENSOR_ADC, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_INPUT_1: OPTIONS_SENSOR_INPUT_1, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if ( model_id in (MODEL_SHELLYPLUG_ID, MODEL_SHELLYPLUG_E_ID) or dev_id_prefix == MODEL_SHELLYPLUG_PREFIX ): model = MODEL_SHELLYPLUG relays = 1 relay_sensors = { SENSOR_POWER: OPTIONS_SENSOR_RELAY_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_RELAY_ENERGY, } relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, } updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYPLUG_US_ID or dev_id_prefix == MODEL_SHELLYPLUG_US_PREFIX: model = MODEL_SHELLYPLUG_US relays = 1 relay_sensors = { SENSOR_POWER: OPTIONS_SENSOR_RELAY_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_RELAY_ENERGY, } relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYPLUG_S_ID or dev_id_prefix == MODEL_SHELLYPLUG_S_PREFIX: model = MODEL_SHELLYPLUG_S relays = 1 relay_sensors = { SENSOR_POWER: OPTIONS_SENSOR_RELAY_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_RELAY_ENERGY, } relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_TEMPERATURE: OPTIONS_SENSOR_DEVICE_TEMPERATURE, } binary_sensors = {SENSOR_OVERTEMPERATURE: OPTIONS_SENSOR_OVERTEMPERATURE} buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLY4PRO_ID or dev_id_prefix == MODEL_SHELLY4PRO_PREFIX: model = MODEL_SHELLY4PRO relays = 4 relay_sensors = { SENSOR_POWER: OPTIONS_SENSOR_RELAY_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_RELAY_ENERGY, } relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} binary_sensors = { SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_INPUT_1: OPTIONS_SENSOR_INPUT_1, SENSOR_INPUT_2: OPTIONS_SENSOR_INPUT_2, SENSOR_INPUT_3: OPTIONS_SENSOR_INPUT_3, } sensors = {SENSOR_IP: OPTIONS_SENSOR_IP} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYHT_ID or dev_id_prefix == MODEL_SHELLYHT_PREFIX: model = MODEL_SHELLYHT sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY, SENSOR_HUMIDITY: OPTIONS_SENSOR_HUMIDITY, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TEMPERATURE_F: OPTIONS_SENSOR_TEMPERATURE_F, SENSOR_TEMPERATURE: OPTIONS_SENSOR_TEMPERATURE, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = {SENSOR_CLOUD: OPTIONS_SENSOR_CLOUD} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE_BATTERY_POWERED} battery_powered = True if model_id == MODEL_SHELLYMOTION_ID or dev_id_prefix == MODEL_SHELLYMOTION_PREFIX: model = MODEL_SHELLYMOTION buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY_MOTION, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_LUX: OPTIONS_SENSOR_LUX_MOTION, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_MOTION: OPTIONS_SENSOR_MOTION_MOTION, SENSOR_VIBRATION: OPTIONS_SENSOR_VIBRATION_MOTION, SENSOR_CHARGER: OPTIONS_SENSOR_CHARGER, SENSOR_CLOUD: OPTIONS_SENSOR_CLOUD, } updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} battery_powered = True if model_id == MODEL_SHELLYMOTION2_ID or dev_id_prefix == MODEL_SHELLYMOTION2_PREFIX: model = MODEL_SHELLYMOTION2 buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY_MOTION, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_LUX: OPTIONS_SENSOR_LUX_MOTION, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TEMPERATURE_F: OPTIONS_SENSOR_TEMPERATURE_MOTION_F, SENSOR_TEMPERATURE: OPTIONS_SENSOR_TEMPERATURE_MOTION, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_MOTION: OPTIONS_SENSOR_MOTION_MOTION, SENSOR_VIBRATION: OPTIONS_SENSOR_VIBRATION_MOTION, SENSOR_CHARGER: OPTIONS_SENSOR_CHARGER, SENSOR_CLOUD: OPTIONS_SENSOR_CLOUD, } updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} battery_powered = True if model_id == MODEL_SHELLYGAS_ID or dev_id_prefix == MODEL_SHELLYGAS_PREFIX: model = MODEL_SHELLYGAS sensors = { SENSOR_CONCENTRATION: OPTIONS_SENSOR_CONCENTRATION, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_OPERATION: OPTIONS_SENSOR_OPERATION, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SELF_TEST: OPTIONS_SENSOR_SELF_TEST, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_VALVE: OPTIONS_SENSOR_VALVE, } binary_sensors = {SENSOR_GAS: OPTIONS_SENSOR_GAS} buttons = { BUTTON_MUTE: OPTIONS_BUTTON_MUTE, BUTTON_RESTART: OPTIONS_BUTTON_RESTART, BUTTON_SELF_TEST: OPTIONS_BUTTON_SELF_TEST, BUTTON_UNMUTE: OPTIONS_BUTTON_UNMUTE, BUTTON_VALVE_CLOSE: {}, BUTTON_VALVE_OPEN: {}, } updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} valves = {VALVE_GAS: OPTIONS_VALVE_GAS} if ( model_id in (MODEL_SHELLYBUTTON1_ID, MODEL_SHELLYBUTTON1V2_ID) or dev_id_prefix == MODEL_SHELLYBUTTON1_PREFIX ): model = MODEL_SHELLYBUTTON1 inputs = 1 inputs_types = [ VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS, VALUE_BUTTON_DOUBLE_PRESS, VALUE_BUTTON_TRIPLE_PRESS, ] sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_CHARGER: OPTIONS_SENSOR_CHARGER_BUTTON, } updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE_BATTERY_POWERED} battery_powered = True if model_id == MODEL_SHELLYDW_ID or dev_id_prefix == MODEL_SHELLYDW_PREFIX: model = MODEL_SHELLYDW sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_LUX: OPTIONS_SENSOR_LUX, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TILT: OPTIONS_SENSOR_TILT, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_OPENING: OPTIONS_SENSOR_OPENING, SENSOR_VIBRATION: OPTIONS_SENSOR_VIBRATION_DW, } updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE_BATTERY_POWERED} battery_powered = True if model_id == MODEL_SHELLYDW2_ID or dev_id_prefix == MODEL_SHELLYDW2_PREFIX: model = MODEL_SHELLYDW2 sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_LUX: OPTIONS_SENSOR_LUX, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TEMPERATURE_F: OPTIONS_SENSOR_TEMPERATURE_F, SENSOR_TEMPERATURE: OPTIONS_SENSOR_TEMPERATURE, SENSOR_TILT: OPTIONS_SENSOR_TILT, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_OPENING: OPTIONS_SENSOR_OPENING, SENSOR_VIBRATION: OPTIONS_SENSOR_VIBRATION_DW, } updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE_BATTERY_POWERED} battery_powered = True if model_id == MODEL_SHELLYSMOKE_ID or dev_id_prefix == MODEL_SHELLYSMOKE_PREFIX: model = MODEL_SHELLYSMOKE sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TEMPERATURE: OPTIONS_SENSOR_TEMPERATURE, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = {SENSOR_SMOKE: OPTIONS_SENSOR_SMOKE} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE_BATTERY_POWERED} battery_powered = True if model_id == MODEL_SHELLYSENSE_ID or dev_id_prefix == MODEL_SHELLYSENSE_PREFIX: model = MODEL_SHELLYSENSE sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_LUX: OPTIONS_SENSOR_LUX, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TEMPERATURE: OPTIONS_SENSOR_TEMPERATURE, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_MOTION: OPTIONS_SENSOR_MOTION, SENSOR_CHARGER: OPTIONS_SENSOR_CHARGER_SENSE, } battery_powered = True if model_id == MODEL_SHELLYRGBW2_ID or dev_id_prefix == MODEL_SHELLYRGBW2_PREFIX: if mode not in (LIGHT_COLOR, LIGHT_WHITE): raise ValueError(f"mode value {mode} is not valid, check script configuration") model = MODEL_SHELLYRGBW2 inputs = 1 rgbw_lights = 1 white_lights = 4 white_lights = { 0: { KEY_COMMAND_ON_TEMPLATE: TPL_COMMAND_ON_WHITE_LIGHT, KEY_COMMAND_TOPIC: TOPIC_WHITE_SET, KEY_STATE_TOPIC: TOPIC_WHITE_STATUS, }, 1: { KEY_COMMAND_ON_TEMPLATE: TPL_COMMAND_ON_WHITE_LIGHT, KEY_COMMAND_TOPIC: TOPIC_WHITE_SET, KEY_STATE_TOPIC: TOPIC_WHITE_STATUS, }, 2: { KEY_COMMAND_ON_TEMPLATE: TPL_COMMAND_ON_WHITE_LIGHT, KEY_COMMAND_TOPIC: TOPIC_WHITE_SET, KEY_STATE_TOPIC: TOPIC_WHITE_STATUS, }, 3: { KEY_COMMAND_ON_TEMPLATE: TPL_COMMAND_ON_WHITE_LIGHT, KEY_COMMAND_TOPIC: TOPIC_WHITE_SET, KEY_STATE_TOPIC: TOPIC_WHITE_STATUS, }, } binary_sensors = {SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0} inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] if mode == LIGHT_COLOR: light_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_COLOR_LIGHT_OVERPOWER} light_sensors = { SENSOR_POWER: OPTIONS_SENSOR_LIGHT_POWER_RGBW2_COLOR, SENSOR_ENERGY: OPTIONS_SENSOR_LIGHT_ENERGY_RGBW2_COLOR, } else: light_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_WHITE_LIGHT_OVERPOWER} light_sensors = { SENSOR_POWER: OPTIONS_SENSOR_LIGHT_POWER_RGBW2_WHITE, SENSOR_ENERGY: OPTIONS_SENSOR_LIGHT_ENERGY_RGBW2_WHITE, } sensors = { SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYDIMMER_ID or dev_id_prefix == MODEL_SHELLYDIMMER_PREFIX: model = MODEL_SHELLYDIMMER inputs = 2 white_lights = { 0: { KEY_COMMAND_ON_TEMPLATE: TPL_COMMAND_ON_WHITE_LIGHT, KEY_COMMAND_TOPIC: TOPIC_LIGHT_SET, KEY_STATE_TOPIC: TOPIC_LIGHT_STATUS, } } inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] sensors = { SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TEMPERATURE: OPTIONS_SENSOR_DEVICE_TEMPERATURE, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_OVERTEMPERATURE: OPTIONS_SENSOR_OVERTEMPERATURE, SENSOR_OVERLOAD: OPTIONS_SENSOR_OVERLOAD, SENSOR_LOADERROR: OPTIONS_SENSOR_LOADERROR, SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_INPUT_1: OPTIONS_SENSOR_INPUT_1, } light_sensors = { SENSOR_POWER: OPTIONS_SENSOR_LIGHT_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_LIGHT_ENERGY, SENSOR_OVERPOWER_VALUE: OPTIONS_SENSOR_LIGHT_OVERPOWER_VALUE, } light_numbers = {NUMBER_LIGHT_BRIGHTNESS: OPTIONS_NUMBER_LIGHT_BRIGHTNESS} buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYDIMMER2_ID or dev_id_prefix == MODEL_SHELLYDIMMER2_PREFIX: model = MODEL_SHELLYDIMMER2 inputs = 2 inputs_types = [VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS] white_lights = { 0: { KEY_COMMAND_ON_TEMPLATE: TPL_COMMAND_ON_WHITE_LIGHT, KEY_COMMAND_TOPIC: TOPIC_LIGHT_SET, KEY_STATE_TOPIC: TOPIC_LIGHT_STATUS, } } sensors = { SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TEMPERATURE: OPTIONS_SENSOR_DEVICE_TEMPERATURE, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = { SENSOR_OVERTEMPERATURE: OPTIONS_SENSOR_OVERTEMPERATURE, SENSOR_OVERLOAD: OPTIONS_SENSOR_OVERLOAD, SENSOR_LOADERROR: OPTIONS_SENSOR_LOADERROR, SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_INPUT_1: OPTIONS_SENSOR_INPUT_1, } light_sensors = { SENSOR_POWER: OPTIONS_SENSOR_LIGHT_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_LIGHT_ENERGY, SENSOR_OVERPOWER_VALUE: OPTIONS_SENSOR_LIGHT_OVERPOWER_VALUE, } light_numbers = {NUMBER_LIGHT_BRIGHTNESS: OPTIONS_NUMBER_LIGHT_BRIGHTNESS} buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYDUO_ID or dev_id_prefix == MODEL_SHELLYDUO_PREFIX: model = MODEL_SHELLYDUO white_lights = { 0: { KEY_COLOR_TEMP_TEMPLATE: TPL_COLOR_TEMP_WHITE_LIGHT, KEY_COMMAND_ON_TEMPLATE: TPL_COMMAND_ON_WHITE_LIGHT_DUO, KEY_COMMAND_TOPIC: TOPIC_LIGHT_SET, KEY_MAX_MIREDS: 370, KEY_MIN_MIREDS: 153, KEY_STATE_TOPIC: TOPIC_LIGHT_STATUS, } } light_sensors = { SENSOR_POWER: OPTIONS_SENSOR_LIGHT_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_LIGHT_ENERGY, } sensors = { SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYVINTAGE_ID or dev_id_prefix == MODEL_SHELLYVINTAGE_PREFIX: model = MODEL_SHELLYVINTAGE white_lights = { 0: { KEY_COMMAND_ON_TEMPLATE: TPL_COMMAND_ON_WHITE_LIGHT, KEY_COMMAND_TOPIC: TOPIC_LIGHT_SET, KEY_STATE_TOPIC: TOPIC_LIGHT_STATUS, } } light_sensors = { SENSOR_POWER: OPTIONS_SENSOR_LIGHT_POWER, SENSOR_ENERGY: OPTIONS_SENSOR_LIGHT_ENERGY, } sensors = { SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYEM_ID or dev_id_prefix == MODEL_SHELLYEM_PREFIX: model = MODEL_SHELLYEM relays = 1 meters = 2 relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} meter_sensors = { SENSOR_ENERGY: OPTIONS_SENSOR_ENERGY_METER, SENSOR_POWER: OPTIONS_SENSOR_POWER_METER, SENSOR_REACTIVE_POWER: OPTIONS_SENSOR_REACTIVE_POWER_METER, SENSOR_RETURNED_ENERGY: OPTIONS_SENSOR_RETURNED_ENERGY_METER, SENSOR_TOTAL_RETURNED: OPTIONS_SENSOR_TOTAL_RETURNED_METER, SENSOR_TOTAL: OPTIONS_SENSOR_TOTAL_METER, SENSOR_VOLTAGE: OPTIONS_SENSOR_VOLTAGE_METER, } sensors = { SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLY3EM_ID or dev_id_prefix == MODEL_SHELLY3EM_PREFIX: model = MODEL_SHELLY3EM relays = 1 meters = 3 relay_binary_sensors = {SENSOR_OVERPOWER: OPTIONS_SENSOR_OVERPOWER} meter_sensors = { SENSOR_CURRENT: OPTIONS_SENSOR_CURRENT_METER, SENSOR_ENERGY: OPTIONS_SENSOR_ENERGY_METER, SENSOR_POWER_FACTOR: OPTIONS_SENSOR_POWER_FACTOR_METER, SENSOR_POWER: OPTIONS_SENSOR_POWER_METER, SENSOR_RETURNED_ENERGY: OPTIONS_SENSOR_RETURNED_ENERGY_METER, SENSOR_TOTAL_RETURNED: OPTIONS_SENSOR_TOTAL_RETURNED_METER, SENSOR_TOTAL: OPTIONS_SENSOR_TOTAL_METER, SENSOR_VOLTAGE: OPTIONS_SENSOR_VOLTAGE_METER, } sensors = { SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_N_CURRENT: OPTIONS_SENSOR_N_CURRENT, SENSOR_IX_SUM_CURRENT: OPTIONS_SENSOR_IX_SUM_CURRENT, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYFLOOD_ID or dev_id_prefix == MODEL_SHELLYFLOOD_PREFIX: model = MODEL_SHELLYFLOOD sensors = { SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_TEMPERATURE_F: OPTIONS_SENSOR_TEMPERATURE_F, SENSOR_TEMPERATURE: OPTIONS_SENSOR_TEMPERATURE, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, } binary_sensors = {SENSOR_FLOOD: OPTIONS_SENSOR_FLOOD} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE_BATTERY_POWERED} battery_powered = True if model_id == MODEL_SHELLYI3_ID or dev_id_prefix == MODEL_SHELLYI3_PREFIX: model = MODEL_SHELLYI3 inputs = 3 inputs_types = [ VALUE_BUTTON_LONG_PRESS, VALUE_BUTTON_SHORT_PRESS, VALUE_BUTTON_DOUBLE_PRESS, VALUE_BUTTON_TRIPLE_PRESS, VALUE_BUTTON_SHORT_LONG_PRESS, VALUE_BUTTON_LONG_SHORT_PRESS, ] binary_sensors = { SENSOR_INPUT_0: OPTIONS_SENSOR_INPUT_0, SENSOR_INPUT_1: OPTIONS_SENSOR_INPUT_1, SENSOR_INPUT_2: OPTIONS_SENSOR_INPUT_2, } sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_UPTIME: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP_VALVE, SENSOR_TEMPERATURE_STATUS: OPTIONS_SENSOR_TEMPERATURE_STATUS, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} if model_id == MODEL_SHELLYVALVE_ID: model = MODEL_SHELLYVALVE battery_powered = True climate_entity_option = { KEY_MAX_TEMP: 31, KEY_MIN_TEMP: 4, KEY_MODES: ["heat", "off"], KEY_PRECISION: 0.1, KEY_TEMP_STEP: 0.5, } sensors = { SENSOR_RSSI: OPTIONS_SENSOR_RSSI, SENSOR_SSID: OPTIONS_SENSOR_SSID, SENSOR_LAST_RESTART: OPTIONS_SENSOR_UPTIME, SENSOR_IP: OPTIONS_SENSOR_IP, SENSOR_BATTERY: OPTIONS_SENSOR_BATTERY_VALVE, } binary_sensors = { SENSOR_CHARGER: OPTIONS_SENSOR_CHARGER, SENSOR_CLOUD: OPTIONS_SENSOR_CLOUD, SENSOR_CALIBRATED: OPTIONS_SENSOR_CALIBRATED, SENSOR_REPORTED_WINDOW_STATE: OPTIONS_SENSOR_REPORTED_WINDOW_STATE, SENSOR_WINDOW_STATE_REPORTING: OPTIONS_SENSOR_WINDOW_STATE_REPORTING, SENSOR_AUTOMATIC_TEMPERATURE_CONTROL: OPTIONS_SENSOR_AUTOMATIC_TEMPERATURE_CONTROL, } buttons = {BUTTON_RESTART: OPTIONS_BUTTON_RESTART} selectors = {SELECT_PROFILES: OPTIONS_SELECT_PROFILES} switches = { SWITCH_SCHEDULE: OPTIONS_SWITCH_SCHEDULE, SWITCH_ACCELERATED_HEATING: OPTIONS_SWITCH_ACCELERATED_HEATING, } numbers = { NUMBER_VALVE_POSITION: OPTIONS_NUMBER_VALVE_POSITION, NUMBER_MINIMAL_VALVE_POSITION: OPTIONS_NUMBER_MINIMAL_VALVE_POSITION, NUMBER_BOOST_TIME: OPTIONS_BOOST_TIME, } updates = {UPDATE_FIRMWARE: OPTIONS_UPDATE_FIRMWARE} device_config = get_device_config(dev_id) if device_config.get(CONF_DEVICE_NAME): device_name = device_config[CONF_DEVICE_NAME] elif ignore_device_model: device_name = format_device_name(dev_id) else: device_name = f"{model} {dev_id.split('-')[-1]}" device_info = { KEY_CONNECTIONS: [[KEY_MAC, format_mac(mac)]], KEY_NAME: device_name, KEY_MODEL: model, KEY_SW_VERSION: fw_ver, KEY_HW_VERSION: f"gen1 ({model_id})", KEY_MANUFACTURER: ATTR_MANUFACTURER, KEY_CONFIGURATION_URL: f"http://{host}/", } origin_info = { KEY_NAME: "Shellies Discovery", KEY_SW_VERSION: VERSION, KEY_SUPPORT_URL: "https://github.com/bieniu/ha-shellies-discovery", } default_topic = f"shellies/{dev_id}/" if battery_powered: if model == MODEL_SHELLYMOTION: expire_after = device_config.get( CONF_EXPIRE_AFTER, EXPIRE_AFTER_FOR_SHELLY_MOTION ) elif model == MODEL_SHELLYVALVE: expire_after = device_config.get( CONF_EXPIRE_AFTER, EXPIRE_AFTER_FOR_SHELLY_VALVE ) elif device_config.get(CONF_POWERED) == ATTR_POWER_AC and model in ( MODEL_SHELLYBUTTON1, MODEL_SHELLYSENSE, ): no_battery_sensor = True battery_powered = False elif device_config.get(CONF_POWERED): no_battery_sensor = True expire_after = device_config.get(CONF_EXPIRE_AFTER, EXPIRE_AFTER_FOR_AC_POWERED) else: expire_after = device_config.get( CONF_EXPIRE_AFTER, EXPIRE_AFTER_FOR_BATTERY_POWERED ) if expire_after and not isinstance(expire_after, int): raise TypeError( f"expire_after value {expire_after} is not an integer, check script configuration" ) availability = [ { KEY_TOPIC: TOPIC_ONLINE, KEY_PAYLOAD_AVAILABLE: "true", KEY_PAYLOAD_NOT_AVAILABLE: "false", }, { KEY_TOPIC: TOPIC_INFO, KEY_VALUE_TEMPLATE: TPL_MQTT_CONNECTED, }, ] # updates for update, update_options in updates.items(): config_topic = f"{disc_prefix}/update/{dev_id}-{update}/config".encode( "ascii", "ignore" ).decode("utf-8") payload = { KEY_NAME: format_entity_name(update), KEY_STATE_TOPIC: update_options[KEY_STATE_TOPIC], KEY_VALUE_TEMPLATE: update_options[KEY_VALUE_TEMPLATE], KEY_LATEST_VERSION_TOPIC: update_options[KEY_LATEST_VERSION_TOPIC], KEY_LATEST_VERSION_TEMPLATE: update_options[KEY_LATEST_VERSION_TEMPLATE], KEY_ENTITY_PICTURE: "https://brands.home-assistant.io/_/shelly/icon.png", KEY_RELEASE_URL: "https://shelly-api-docs.shelly.cloud/gen1/#changelog", KEY_TITLE: "Firmware", KEY_DEVICE_CLASS: DEVICE_CLASS_FIRMWARE, KEY_ENABLED_BY_DEFAULT: str(update_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-{update}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if battery_powered and model not in (MODEL_SHELLYDW, MODEL_SHELLYDW2): payload[KEY_EXPIRE_AFTER] = expire_after elif not battery_powered: payload[KEY_AVAILABILITY] = availability if update_options.get(KEY_COMMAND_TOPIC): payload[KEY_COMMAND_TOPIC] = update_options[KEY_COMMAND_TOPIC] payload[KEY_PAYLOAD_INSTALL] = update_options[KEY_PAYLOAD_INSTALL] if update_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = update_options[KEY_ENTITY_CATEGORY] if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # numbers for number, number_options in numbers.items(): config_topic = f"{disc_prefix}/number/{dev_id}-{number}/config".encode( "ascii", "ignore" ).decode("utf-8") if number_options.get(ATTR_AVAILABILITY_EXTRA): availability.append(number_options[ATTR_AVAILABILITY_EXTRA]) payload = { KEY_NAME: format_entity_name(number), KEY_COMMAND_TOPIC: number_options[KEY_COMMAND_TOPIC], KEY_MAX: number_options[KEY_MAX], KEY_MIN: number_options[KEY_MIN], KEY_STEP: number_options[KEY_STEP], KEY_STATE_TOPIC: number_options[KEY_STATE_TOPIC], KEY_VALUE_TEMPLATE: number_options[KEY_VALUE_TEMPLATE], KEY_UNIT: number_options[KEY_UNIT], KEY_ENABLED_BY_DEFAULT: str(number_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-{number}".lower(), KEY_QOS: qos, KEY_AVAILABILITY: availability, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if number_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = number_options[KEY_ENTITY_CATEGORY] if number_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = number_options[KEY_DEVICE_CLASS] if number_options.get(ATTR_ICON): payload[KEY_ICON] = number_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # switches (not relays) # noqa: ERA001 for switch, switch_options in switches.items(): config_topic = f"{disc_prefix}/switch/{dev_id}-{switch}/config".encode( "ascii", "ignore" ).decode("utf-8") payload = { KEY_NAME: format_entity_name(switch), KEY_COMMAND_TOPIC: switch_options[KEY_COMMAND_TOPIC], KEY_PAYLOAD_OFF: switch_options[KEY_PAYLOAD_OFF], KEY_PAYLOAD_ON: switch_options[KEY_PAYLOAD_ON], KEY_STATE_TOPIC: switch_options[KEY_STATE_TOPIC], KEY_STATE_OFF: switch_options[KEY_STATE_OFF], KEY_STATE_ON: switch_options[KEY_STATE_ON], KEY_VALUE_TEMPLATE: switch_options[KEY_VALUE_TEMPLATE], KEY_ENABLED_BY_DEFAULT: str(switch_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-{switch}".lower(), KEY_QOS: qos, KEY_AVAILABILITY: availability, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if switch_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = switch_options[KEY_ENTITY_CATEGORY] if switch_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = switch_options[KEY_DEVICE_CLASS] if switch_options.get(ATTR_ICON): payload[KEY_ICON] = switch_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # selectors for select, select_options in selectors.items(): config_topic = f"{disc_prefix}/select/{dev_id}-{select}/config".encode( "ascii", "ignore" ).decode("utf-8") payload = { KEY_NAME: format_entity_name(select), KEY_COMMAND_TOPIC: select_options[KEY_COMMAND_TOPIC], KEY_COMMAND_TEMPLATE: TPL_COMMAND_PROFILES, KEY_OPTIONS: select_options[KEY_OPTIONS], KEY_STATE_TOPIC: select_options[KEY_STATE_TOPIC], KEY_VALUE_TEMPLATE: select_options[KEY_VALUE_TEMPLATE], KEY_ENABLED_BY_DEFAULT: str(select_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-{select}".lower(), KEY_QOS: qos, KEY_AVAILABILITY: availability, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if select_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = select_options[KEY_ENTITY_CATEGORY] if select_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = select_options[KEY_DEVICE_CLASS] if select_options.get(ATTR_ICON): payload[KEY_ICON] = select_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # buttons for button, button_options in buttons.items(): config_topic = f"{disc_prefix}/button/{dev_id}-{button}/config".encode( "ascii", "ignore" ).decode("utf-8") payload = { KEY_NAME: format_entity_name(button), KEY_COMMAND_TOPIC: button_options.get(KEY_COMMAND_TOPIC), KEY_PAYLOAD_PRESS: button_options.get(KEY_PAYLOAD_PRESS), KEY_ENABLED_BY_DEFAULT: str( button_options.get(KEY_ENABLED_BY_DEFAULT, False) ).lower(), KEY_UNIQUE_ID: f"{dev_id}-{button}".lower(), KEY_QOS: qos, KEY_AVAILABILITY: availability, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if button_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = button_options[KEY_ENTITY_CATEGORY] if button_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = button_options[KEY_DEVICE_CLASS] if button_options.get(ATTR_ICON): payload[KEY_ICON] = button_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" if not button_options: payload = "" mqtt_publish(config_topic, payload, retain) # climate entities if climate_entity_option: default_heat_temp = 20 minimal_valve_position = device_config.get(CONF_MINIMAL_VALVE_POSITION, 0) if not isinstance(minimal_valve_position, int): raise TypeError( f"minimal_valve_position value {minimal_valve_position} is not an integer, check script configuration" ) if device_config.get(CONF_DEFAULT_HEAT_TEMP): value = device_config[CONF_DEFAULT_HEAT_TEMP] if ( climate_entity_option[KEY_MIN_TEMP] < value < climate_entity_option[KEY_MAX_TEMP] ): default_heat_temp = value temperature_command_topic = "~thermostat/0/command/target_t" config_topic = f"{disc_prefix}/climate/{dev_id}/config".encode( "ascii", "ignore" ).decode("utf-8") availability.append( { KEY_TOPIC: TOPIC_INFO, KEY_VALUE_TEMPLATE: TPL_CALIBRATED_AVAILABILITY, } ) payload = { KEY_ACTION_TOPIC: TOPIC_INFO, KEY_ACTION_TEMPLATE: TPL_ACTION_TEMPLATE.format( min_temp=climate_entity_option[KEY_MIN_TEMP], min_pos=minimal_valve_position, ), KEY_CURRENT_TEMPERATURE_TOPIC: TOPIC_INFO, KEY_CURRENT_TEMPERATURE_TEMPLATE: TPL_CURRENT_TEMPERATURE, KEY_TEMPERATURE_STATE_TOPIC: TOPIC_INFO, KEY_TEMPERATURE_STATE_TEMPLATE: TPL_TARGET_TEMPERATURE, KEY_TEMPERATURE_COMMAND_TOPIC: temperature_command_topic, KEY_TEMPERATURE_COMMAND_TEMPLATE: TPL_SET_TARGET_TEMPERATURE, KEY_TEMP_STEP: climate_entity_option[KEY_TEMP_STEP], KEY_MODE_STATE_TOPIC: TOPIC_INFO, KEY_MODE_COMMAND_TOPIC: temperature_command_topic, KEY_MODE_COMMAND_TEMPLATE: TPL_MODE_SET.format( default_heat_temp=default_heat_temp ), KEY_MODE_STATE_TEMPLATE: TPL_MODE, KEY_UNIQUE_ID: f"{dev_id}".lower(), KEY_OPTIMISTIC: VALUE_FALSE, KEY_QOS: qos, KEY_AVAILABILITY: availability, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } payload.update(climate_entity_option) if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # rollers for roller_id in range(rollers): if device_config.get(CONF_POSITION_TEMPLATE): position_template = device_config[CONF_POSITION_TEMPLATE] else: position_template = TPL_POSITION set_position_template = device_config.get(CONF_SET_POSITION_TEMPLATE, None) if device_config.get(f"roller-{roller_id}-name"): roller_name = device_config[f"roller-{roller_id}-name"] else: roller_name = f"Roller {roller_id}" device_class = None if device_config.get(f"roller-{roller_id}-class"): if device_config[f"roller-{roller_id}-class"] in ROLLER_DEVICE_CLASSES: device_class = device_config[f"roller-{roller_id}-class"] else: wrong_class = device_config[f"roller-{roller_id}-class"] logger.error( # noqa: F821 "%s is the wrong roller class, the default value None was used", wrong_class, ) state_topic = f"~roller/{roller_id}" config_topic = f"{disc_prefix}/cover/{dev_id}-roller-{roller_id}/config".encode( "ascii", "ignore" ).decode("utf-8") if roller_mode: payload = { KEY_NAME: roller_name, KEY_COMMAND_TOPIC: f"{state_topic}/command", KEY_POSITION_TOPIC: f"{state_topic}/pos", KEY_STATE_TOPIC: state_topic, KEY_STATE_CLOSING: VALUE_CLOSE, KEY_STATE_OPENING: VALUE_OPEN, KEY_STATE_STOPPED: VALUE_STOP, KEY_POSITION_TEMPLATE: position_template, KEY_SET_POSITION_TOPIC: f"{state_topic}/command/pos", KEY_PAYLOAD_OPEN: VALUE_OPEN, KEY_PAYLOAD_CLOSE: VALUE_CLOSE, KEY_PAYLOAD_STOP: VALUE_STOP, KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: f"{dev_id}-roller-{roller_id}".lower(), KEY_OPTIMISTIC: str(optimistic).lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if set_position_template: payload[KEY_SET_POSITION_TEMPLATE] = set_position_template if device_class: payload[KEY_DEVICE_CLASS] = device_class else: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # relays for relay_id in range(relays): if device_config.get(f"relay-{relay_id}-name"): relay_name = device_config[f"relay-{relay_id}-name"] else: relay_name = f"Relay {relay_id}" state_topic = f"~relay/{relay_id}" config_component = COMP_SWITCH if device_config.get(f"relay-{relay_id}"): config_component = device_config[f"relay-{relay_id}"] for component in relay_components: config_topic = ( f"{disc_prefix}/{component}/{dev_id}-relay-{relay_id}/config".encode( "ascii", "ignore" ).decode("utf-8") ) if component == config_component and not roller_mode: payload = { KEY_NAME: relay_name, KEY_COMMAND_TOPIC: f"{state_topic}/command", KEY_STATE_TOPIC: state_topic, KEY_PAYLOAD_OFF: VALUE_OFF, KEY_PAYLOAD_ON: VALUE_ON, KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: f"{dev_id}-relay-{relay_id}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } else: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # relay sensors for sensor, sensor_options in relay_sensors.items(): force_update = False if isinstance(device_config.get(CONF_FORCE_UPDATE_SENSORS), bool): force_update = device_config.get(CONF_FORCE_UPDATE_SENSORS) config_topic = ( f"{disc_prefix}/sensor/{dev_id}-{sensor}-{relay_id}/config".encode( "ascii", "ignore" ).decode("utf-8") ) payload = { KEY_NAME: f"{format_entity_name(sensor)} {relay_id}", KEY_STATE_TOPIC: sensor_options[KEY_STATE_TOPIC].format(relay_id=relay_id), KEY_AVAILABILITY: availability, KEY_FORCE_UPDATE: str(force_update).lower(), KEY_ENABLED_BY_DEFAULT: str(sensor_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-relay-{sensor}-{relay_id}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_SUGGESTED_DISPLAY_PRECISION): payload[KEY_SUGGESTED_DISPLAY_PRECISION] = sensor_options[ KEY_SUGGESTED_DISPLAY_PRECISION ] if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if sensor_options.get(KEY_STATE_CLASS): payload[KEY_STATE_CLASS] = sensor_options[KEY_STATE_CLASS] if sensor_options.get(KEY_UNIT): payload[KEY_UNIT] = sensor_options[KEY_UNIT] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] if sensor_options.get(ATTR_ICON): payload[KEY_ICON] = sensor_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" if roller_mode: payload = "" mqtt_publish(config_topic, payload, retain) # relay's binary sensors for sensor, sensor_options in relay_binary_sensors.items(): config_topic = f"{disc_prefix}/binary_sensor/{dev_id}-{make_id(sensor)}-{relay_id}/config".encode( "ascii", "ignore" ).decode("utf-8") if device_config.get(f"relay-{relay_id}-name"): sensor_name = f"{device_config[f'relay-{relay_id}-name']} {format_entity_name(sensor)}" else: sensor_name = f"{format_entity_name(sensor)} {relay_id}" if not roller_mode: payload = { KEY_NAME: sensor_name, KEY_STATE_TOPIC: sensor_options[KEY_STATE_TOPIC].format( relay_id=relay_id ), KEY_ENABLED_BY_DEFAULT: str( sensor_options[KEY_ENABLED_BY_DEFAULT] ).lower(), KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: f"{dev_id}-{make_id(sensor)}-{relay_id}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] else: payload[KEY_PAYLOAD_ON] = sensor_options[KEY_PAYLOAD_ON] payload[KEY_PAYLOAD_OFF] = sensor_options[KEY_PAYLOAD_OFF] if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if ( model in ( MODEL_SHELLY1PM, MODEL_SHELLY2, MODEL_SHELLY25, MODEL_SHELLY4PRO, MODEL_SHELLYPLUG, MODEL_SHELLYPLUG_S, MODEL_SHELLYPLUG_US, ) and sensor == SENSOR_OVERPOWER ): payload[KEY_JSON_ATTRIBUTES_TOPIC] = ( f"~{sensor}/{relay_id}/{TOPIC_OVERPOWER_VALUE}" ) payload[KEY_JSON_ATTRIBUTES_TEMPLATE] = TPL_OVERPOWER_VALUE_TO_JSON else: payload = "" if dev_id.lower() in ignored: payload = "" if not sensor_options: payload = "" mqtt_publish(config_topic, payload, retain) # sensors for sensor, sensor_options in sensors.items(): use_fahrenheit = device_config.get(CONF_USE_FAHRENHEIT) force_update = False if isinstance(device_config.get(CONF_FORCE_UPDATE_SENSORS), bool): force_update = device_config.get(CONF_FORCE_UPDATE_SENSORS) config_topic = f"{disc_prefix}/sensor/{dev_id}-{sensor}/config".encode( "ascii", "ignore" ).decode("utf-8") if model in (MODEL_SHELLY2, MODEL_SHELLY25) and sensor in ( SENSOR_ENERGY, SENSOR_POWER, ): unique_id = f"{dev_id}-relay-{sensor}".lower() else: unique_id = f"{dev_id}-{sensor}".lower() if sensor in (SENSOR_SSID, SENSOR_ADC): sensor_name = sensor.upper() elif sensor == SENSOR_IP: sensor_name = "IP address" elif sensor == SENSOR_IX_SUM_CURRENT: sensor_name = "IX sum current" elif sensor == SENSOR_UPTIME: sensor_name = "Last restart" elif sensor == SENSOR_RSSI: sensor_name = "WiFi signal" elif sensor == SENSOR_TEMPERATURE_F: sensor_name = "Temperature" else: sensor_name = format_entity_name(sensor) payload = { KEY_NAME: sensor_name, KEY_STATE_TOPIC: sensor_options[KEY_STATE_TOPIC], KEY_FORCE_UPDATE: str(force_update).lower(), KEY_ENABLED_BY_DEFAULT: str(sensor_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: unique_id, KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_SUGGESTED_DISPLAY_PRECISION): payload[KEY_SUGGESTED_DISPLAY_PRECISION] = sensor_options[ KEY_SUGGESTED_DISPLAY_PRECISION ] if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if sensor_options.get(KEY_STATE_CLASS): payload[KEY_STATE_CLASS] = sensor_options[KEY_STATE_CLASS] if model == MODEL_SHELLYDW2 and sensor == SENSOR_LUX: payload[KEY_JSON_ATTRIBUTES_TOPIC] = f"~sensor/{SENSOR_ILLUMINATION}" payload[KEY_JSON_ATTRIBUTES_TEMPLATE] = TPL_ILLUMINATION_TO_JSON if sensor_options.get(KEY_UNIT): payload[KEY_UNIT] = sensor_options[KEY_UNIT] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] if sensor_options.get(ATTR_ICON): payload[KEY_ICON] = sensor_options[ATTR_ICON] if battery_powered and model not in (MODEL_SHELLYDW, MODEL_SHELLYDW2): payload[KEY_EXPIRE_AFTER] = expire_after elif not battery_powered: payload[KEY_AVAILABILITY] = availability if no_battery_sensor and sensor == SENSOR_BATTERY: payload = "" if use_fahrenheit and sensor == SENSOR_TEMPERATURE: payload = "" if not use_fahrenheit and sensor == SENSOR_TEMPERATURE_F: payload = "" if ( model == MODEL_SHELLY25 and sensor in (SENSOR_ENERGY, SENSOR_POWER) and not roller_mode ): payload = "" if sensor == SENSOR_VALVE and not device_config.get(CONF_VALVE_CONNECTED, False): payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # inputs for input_id in range(inputs): config_topic = f"{disc_prefix}/device_automation/{dev_id}-input-{input_id}/button_release/config".encode( "ascii", "ignore" ).decode("utf-8") topic = f"shellies/{dev_id}/input/{input_id}" payload = { KEY_AUTOMATION_TYPE: VALUE_TRIGGER, KEY_TOPIC: topic, KEY_PAYLOAD: "0", KEY_QOS: qos, KEY_DEVICE: device_info, KEY_TYPE: VALUE_BUTTON_SHORT_RELEASE, KEY_SUBTYPE: f"button_{input_id + 1}", } if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) topic = f"shellies/{dev_id}/input_event/{input_id}" for event in inputs_types: config_topic = f"{disc_prefix}/device_automation/{dev_id}-input-{input_id}/{event}/config".encode( "ascii", "ignore" ).decode("utf-8") payload = { KEY_AUTOMATION_TYPE: VALUE_TRIGGER, KEY_TOPIC: topic, KEY_PAYLOAD: DEVICE_TRIGGERS_MAP[event], KEY_VALUE_TEMPLATE: "{{value_json.event}}", KEY_QOS: qos, KEY_DEVICE: device_info, KEY_TYPE: event, KEY_SUBTYPE: f"button_{input_id + 1}", } if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # events config_topic = f"{disc_prefix}/event/{dev_id}-input-{input_id}/config".encode( "ascii", "ignore" ).decode("utf-8") unique_id = f"{dev_id}-input-{input_id}".lower() payload = { KEY_NAME: f"Button {input_id}", KEY_STATE_TOPIC: f"~input_event/{input_id}", KEY_EVENT_TYPES: list(DEVICE_TRIGGERS_MAP.values()), KEY_VALUE_TEMPLATE: TPL_EVENT, KEY_DEVICE_CLASS: DEVICE_CLASS_BUTTON, KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: unique_id, KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # external temperature sensors for sensor_id in range(ext_temp_sensors): force_update = False if isinstance(device_config.get(CONF_FORCE_UPDATE_SENSORS), bool): force_update = device_config.get(CONF_FORCE_UPDATE_SENSORS) unique_id = f"{dev_id}-ext-temperature-{sensor_id}".lower() config_topic = ( f"{disc_prefix}/sensor/{dev_id}-ext-temperature-{sensor_id}/config".encode( "ascii", "ignore" ).decode("utf-8") ) sensor_name = f"External temperature {sensor_id}" state_topic = f"~{SENSOR_EXT_TEMPERATURE}/{sensor_id}" if device_config.get(f"ext-temperature-{sensor_id}"): payload = { KEY_NAME: sensor_name, KEY_STATE_TOPIC: state_topic, KEY_VALUE_TEMPLATE: TPL_TEMPERATURE_EXT, KEY_SUGGESTED_DISPLAY_PRECISION: 1, KEY_STATE_CLASS: STATE_CLASS_MEASUREMENT, KEY_UNIT: UNIT_CELSIUS, KEY_DEVICE_CLASS: SENSOR_TEMPERATURE, KEY_FORCE_UPDATE: str(force_update).lower(), KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: unique_id, KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } else: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # external humidity sensors for sensor_id in range(ext_humi_sensors): force_update = False if isinstance(device_config.get(CONF_FORCE_UPDATE_SENSORS), bool): force_update = device_config.get(CONF_FORCE_UPDATE_SENSORS) unique_id = f"{dev_id}-ext-humidity-{sensor_id}".lower() config_topic = ( f"{disc_prefix}/sensor/{dev_id}-ext-humidity-{sensor_id}/config".encode( "ascii", "ignore" ).decode("utf-8") ) sensor_name = f"External humidity {sensor_id}" state_topic = f"~{SENSOR_EXT_HUMIDITY}/{sensor_id}" if device_config.get(f"ext-temperature-{sensor_id}"): payload = { KEY_NAME: sensor_name, KEY_STATE_TOPIC: state_topic, KEY_VALUE_TEMPLATE: TPL_HUMIDITY_EXT, KEY_SUGGESTED_DISPLAY_PRECISION: 1, KEY_STATE_CLASS: STATE_CLASS_MEASUREMENT, KEY_UNIT: UNIT_PERCENT, KEY_DEVICE_CLASS: SENSOR_HUMIDITY, KEY_FORCE_UPDATE: str(force_update).lower(), KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: unique_id, KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } else: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # binary sensors for sensor, sensor_options in binary_sensors.items(): config_topic = ( f"{disc_prefix}/binary_sensor/{dev_id}-{make_id(sensor)}/config".encode( "ascii", "ignore" ).decode("utf-8") ) if sensor == SENSOR_EXT_SWITCH: sensor_name = "External switch" else: sensor_name = format_entity_name(sensor) state_topic = sensor_options[KEY_STATE_TOPIC] payload = { KEY_NAME: sensor_name, KEY_STATE_TOPIC: state_topic, KEY_ENABLED_BY_DEFAULT: str(sensor_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-{make_id(sensor)}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_ICON): payload[KEY_ICON] = sensor_options[KEY_ICON] if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] else: payload[KEY_PAYLOAD_ON] = sensor_options.get(KEY_PAYLOAD_ON) payload[KEY_PAYLOAD_OFF] = sensor_options.get(KEY_PAYLOAD_OFF) if battery_powered and model not in (MODEL_SHELLYDW, MODEL_SHELLYDW2): payload[KEY_EXPIRE_AFTER] = expire_after elif not battery_powered: payload[KEY_AVAILABILITY] = availability if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if ( model == MODEL_SHELLYRGBW2 and mode == LIGHT_WHITE and sensor == SENSOR_OVERPOWER ): payload = "" if model in (MODEL_SHELLYDW, MODEL_SHELLYDW2) and sensor == SENSOR_OPENING: payload[KEY_FORCE_UPDATE] = VALUE_TRUE if model == MODEL_SHELLYGAS and sensor == SENSOR_GAS: payload[KEY_JSON_ATTRIBUTES_TOPIC] = state_topic payload[KEY_JSON_ATTRIBUTES_TEMPLATE] = TPL_GAS_TO_JSON if ( model == MODEL_SHELLY1 and sensor == SENSOR_EXT_SWITCH and not device_config.get(CONF_EXT_SWITCH) ): payload = "" if ( model == MODEL_SHELLYHT and sensor == SENSOR_CLOUD and device_config.get(CONF_POWERED) != ATTR_POWER_AC ): payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # color lights for light_id in range(rgbw_lights): if device_config.get(f"light-{light_id}-name"): light_name = device_config[f"light-{light_id}-name"] else: light_name = f"Light {light_id}" state_topic = f"~color/{light_id}" status_topic = f"~color/{light_id}/status" set_topic = f"~color/{light_id}/set" command_topic = f"~color/{light_id}/command" unique_id = f"{dev_id}-light-{light_id}".lower() config_topic = f"{disc_prefix}/light/{dev_id}-{light_id}/config".encode( "ascii", "ignore" ).decode("utf-8") if mode == LIGHT_COLOR and model == MODEL_SHELLYRGBW2: payload = { KEY_NAME: light_name, KEY_AVAILABILITY: availability, KEY_COMMAND_TOPIC: command_topic, KEY_STATE_TOPIC: state_topic, KEY_STATE_VALUE_TEMPLATE: "{{value.lower()}}", KEY_PAYLOAD_ON: VALUE_ON, KEY_PAYLOAD_OFF: VALUE_OFF, KEY_RGBW_COMMAND_TOPIC: set_topic, KEY_RGBW_COMMAND_TEMPLATE: "{^red^:{{red}},^green^:{{green}},^blue^:{{blue}},^white^:{{white}}}", KEY_RGBW_STATE_TOPIC: status_topic, KEY_RGBW_VALUE_TEMPLATE: "{{value_json.red}},{{value_json.green}},{{value_json.blue}},{{value_json.white}}", KEY_BRIGHTNESS_STATE_TOPIC: status_topic, KEY_BRIGHTNESS_VALUE_TEMPLATE: "{{value_json.gain|float|multiply(2.55)|round(0)}}", KEY_BRIGHTNESS_COMMAND_TOPIC: set_topic, KEY_BRIGHTNESS_COMMAND_TEMPLATE: "{^gain^:{{value|float|multiply(0.3922)|round(0)}}}", KEY_EFFECT_COMMAND_TOPIC: set_topic, KEY_EFFECT_COMMAND_TEMPLATE: "{ {%if value==^Off^%}^effect^:0{%elif value==^Meteor Shower^%}^effect^:1{%elif value==^Gradual Change^%}^effect^:2{%elif value==^Flash^%}^effect^:3{%endif%} }", KEY_EFFECT_LIST: ["Off", "Meteor Shower", "Gradual Change", "Flash"], KEY_EFFECT_STATE_TOPIC: status_topic, KEY_EFFECT_VALUE_TEMPLATE: "{%if value_json.effect==1%}Meteor Shower{%elif value_json.effect==2%}Gradual Change{%elif value_json.effect==3%}Flash{%else%}Off{%endif%}", KEY_UNIQUE_ID: unique_id, KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } else: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain, json=True) # color light's binary sensors for sensor, sensor_options in light_binary_sensors.items(): config_topic = f"{disc_prefix}/binary_sensor/{dev_id}-color-{sensor}-{light_id}/config".encode( "ascii", "ignore" ).decode("utf-8") if mode == LIGHT_COLOR: payload = { KEY_NAME: f"{format_entity_name(sensor)} {light_id}", KEY_STATE_TOPIC: sensor_options[KEY_STATE_TOPIC], KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: f"{dev_id}-color-{sensor}-{light_id}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] else: payload[KEY_PAYLOAD_ON] = sensor_options[KEY_PAYLOAD_ON] payload[KEY_PAYLOAD_OFF] = sensor_options[KEY_PAYLOAD_OFF] else: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # color light sensors for sensor, sensor_options in light_sensors.items(): force_update = False if isinstance(device_config.get(CONF_FORCE_UPDATE_SENSORS), bool): force_update = device_config.get(CONF_FORCE_UPDATE_SENSORS) config_topic = ( f"{disc_prefix}/sensor/{dev_id}-color-{sensor}-{light_id}/config".encode( "ascii", "ignore" ).decode("utf-8") ) payload = { KEY_NAME: f"{format_entity_name(sensor)} {light_id}", KEY_STATE_TOPIC: sensor_options[KEY_STATE_TOPIC].format(light_id=light_id), KEY_AVAILABILITY: availability, KEY_FORCE_UPDATE: str(force_update).lower(), KEY_ENABLED_BY_DEFAULT: str(sensor_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-color-{sensor}-{light_id}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_SUGGESTED_DISPLAY_PRECISION): payload[KEY_SUGGESTED_DISPLAY_PRECISION] = sensor_options[ KEY_SUGGESTED_DISPLAY_PRECISION ] if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if sensor_options.get(KEY_STATE_CLASS): payload[KEY_STATE_CLASS] = sensor_options[KEY_STATE_CLASS] if sensor_options.get(KEY_UNIT): payload[KEY_UNIT] = sensor_options[KEY_UNIT] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] if sensor_options.get(ATTR_ICON): payload[KEY_ICON] = sensor_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" if mode == LIGHT_WHITE: payload = "" mqtt_publish(config_topic, payload, retain) # white lights for light_id, light_options in white_lights.items(): if device_config.get(f"light-{light_id}-name"): light_name = device_config[f"light-{light_id}-name"] else: light_name = f"Light {light_id}" if model == MODEL_SHELLYRGBW2: unique_id = f"{dev_id}-light-white-{light_id}".lower() config_topic = f"{disc_prefix}/light/{dev_id}-white-{light_id}/config".encode( "ascii", "ignore" ).decode("utf-8") else: unique_id = f"{dev_id}-light-{light_id}".lower() config_topic = f"{disc_prefix}/light/{dev_id}-{light_id}/config".encode( "ascii", "ignore" ).decode("utf-8") payload = { KEY_SCHEMA: VALUE_TEMPLATE, KEY_NAME: light_name, KEY_COMMAND_TOPIC: light_options[KEY_COMMAND_TOPIC].format(light_id=light_id), KEY_STATE_TOPIC: light_options[KEY_STATE_TOPIC].format(light_id=light_id), KEY_AVAILABILITY: availability, KEY_COMMAND_ON_TEMPLATE: light_options[KEY_COMMAND_ON_TEMPLATE].format( max_transition=MAX_TRANSITION ), KEY_COMMAND_OFF_TEMPLATE: f"{{^turn^:^off^{{%if transition is defined%}},^transition^:{{{{min(transition|multiply(1000),{MAX_TRANSITION})}}}}{{%endif%}}}}", KEY_STATE_TEMPLATE: "{%if value_json.ison%}on{%else%}off{%endif%}", KEY_BRIGHTNESS_TEMPLATE: "{{value_json.brightness|float|multiply(2.55)|round}}", KEY_UNIQUE_ID: unique_id, KEY_QOS: str(qos), KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if light_options.get(KEY_COLOR_TEMP_TEMPLATE): payload[KEY_COLOR_TEMP_TEMPLATE] = TPL_COLOR_TEMP_WHITE_LIGHT if light_options.get(KEY_MAX_MIREDS): payload[KEY_MAX_MIREDS] = light_options[KEY_MAX_MIREDS] if light_options.get(KEY_MIN_MIREDS): payload[KEY_MIN_MIREDS] = light_options[KEY_MIN_MIREDS] if model == MODEL_SHELLYRGBW2 and mode == LIGHT_COLOR: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain, json=True) # white light's binary sensors for sensor, sensor_options in light_binary_sensors.items(): config_topic = f"{disc_prefix}/binary_sensor/{dev_id}-white-{sensor}-{light_id}/config".encode( "ascii", "ignore" ).decode("utf-8") if mode != LIGHT_COLOR: payload = { KEY_NAME: f"{format_entity_name(sensor)} {light_id}", KEY_STATE_TOPIC: sensor_options[KEY_STATE_TOPIC].format( light_id=light_id ), KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: f"{dev_id}-white-{sensor}-{light_id}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] else: payload[KEY_PAYLOAD_ON] = sensor_options[KEY_PAYLOAD_ON] payload[KEY_PAYLOAD_OFF] = sensor_options[KEY_PAYLOAD_OFF] else: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # white light sensors for sensor, sensor_options in light_sensors.items(): force_update = False if isinstance(device_config.get(CONF_FORCE_UPDATE_SENSORS), bool): force_update = device_config.get(CONF_FORCE_UPDATE_SENSORS) config_topic = ( f"{disc_prefix}/sensor/{dev_id}-white-{sensor}-{light_id}/config".encode( "ascii", "ignore" ).decode("utf-8") ) payload = { KEY_NAME: f"{format_entity_name(sensor)} {light_id}", KEY_STATE_TOPIC: sensor_options[KEY_STATE_TOPIC].format(light_id=light_id), KEY_AVAILABILITY: availability, KEY_FORCE_UPDATE: str(force_update).lower(), KEY_ENABLED_BY_DEFAULT: str(sensor_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-white-{sensor}-{light_id}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_SUGGESTED_DISPLAY_PRECISION): payload[KEY_SUGGESTED_DISPLAY_PRECISION] = sensor_options[ KEY_SUGGESTED_DISPLAY_PRECISION ] if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if sensor_options.get(KEY_STATE_CLASS): payload[KEY_STATE_CLASS] = sensor_options[KEY_STATE_CLASS] if sensor_options.get(KEY_UNIT): payload[KEY_UNIT] = sensor_options[KEY_UNIT] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] if sensor_options.get(ATTR_ICON): payload[KEY_ICON] = sensor_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" if mode == LIGHT_COLOR: payload = "" mqtt_publish(config_topic, payload, retain) # light numbers for number, number_options in light_numbers.items(): config_topic = ( f"{disc_prefix}/number/{dev_id}-{light_id}-{number}/config".encode( "ascii", "ignore" ).decode("utf-8") ) payload = { KEY_NAME: f"{format_entity_name(number)} {light_id}", KEY_COMMAND_TOPIC: number_options[KEY_COMMAND_TOPIC].format( light_id=light_id ), KEY_COMMAND_TEMPLATE: number_options[KEY_COMMAND_TEMPLATE].format(), KEY_MAX: number_options[KEY_MAX], KEY_MIN: number_options[KEY_MIN], KEY_STEP: number_options[KEY_STEP], KEY_STATE_TOPIC: number_options[KEY_STATE_TOPIC].format(light_id=light_id), KEY_VALUE_TEMPLATE: number_options[KEY_VALUE_TEMPLATE], KEY_UNIT: number_options[KEY_UNIT], KEY_ENABLED_BY_DEFAULT: str(number_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-{number}-{light_id}".lower(), KEY_QOS: qos, KEY_AVAILABILITY: availability, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if number_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = number_options[KEY_ENTITY_CATEGORY] if number_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = number_options[KEY_DEVICE_CLASS] if number_options.get(ATTR_ICON): payload[KEY_ICON] = number_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain, json=True) # meter sensors for meter_id in range(meters): force_update = False if isinstance(device_config.get(CONF_FORCE_UPDATE_SENSORS), bool): force_update = device_config.get(CONF_FORCE_UPDATE_SENSORS) for sensor, sensor_options in meter_sensors.items(): config_topic = ( f"{disc_prefix}/sensor/{dev_id}-emeter-{sensor}-{meter_id}/config".encode( "ascii", "ignore" ).decode("utf-8") ) payload = { KEY_NAME: f"{format_entity_name(sensor)} {meter_id}", KEY_STATE_TOPIC: sensor_options[KEY_STATE_TOPIC].format(meter_id=meter_id), KEY_AVAILABILITY: availability, KEY_FORCE_UPDATE: str(force_update).lower(), KEY_ENABLED_BY_DEFAULT: str(sensor_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_UNIQUE_ID: f"{dev_id}-emeter-{sensor}-{meter_id}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if sensor_options.get(KEY_SUGGESTED_DISPLAY_PRECISION): payload[KEY_SUGGESTED_DISPLAY_PRECISION] = sensor_options[ KEY_SUGGESTED_DISPLAY_PRECISION ] if sensor_options.get(KEY_ENTITY_CATEGORY): payload[KEY_ENTITY_CATEGORY] = sensor_options[KEY_ENTITY_CATEGORY] if sensor_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = sensor_options[KEY_DEVICE_CLASS] if sensor_options.get(KEY_STATE_CLASS): payload[KEY_STATE_CLASS] = sensor_options[KEY_STATE_CLASS] if sensor_options.get(KEY_UNIT): payload[KEY_UNIT] = sensor_options[KEY_UNIT] if sensor_options.get(KEY_VALUE_TEMPLATE): payload[KEY_VALUE_TEMPLATE] = sensor_options[KEY_VALUE_TEMPLATE] if sensor_options.get(ATTR_ICON): payload[KEY_ICON] = sensor_options[ATTR_ICON] if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain) # valves for valve, valve_options in valves.items(): config_topic = f"{disc_prefix}/valve/{dev_id}-{make_id(valve)}/config".encode( "ascii", "ignore" ).decode("utf-8") if valve_options.get(ATTR_AVAILABILITY_EXTRA): availability.append(valve_options[ATTR_AVAILABILITY_EXTRA]) payload = { KEY_NAME: valve_options.get(KEY_NAME), KEY_COMMAND_TOPIC: valve_options.get(KEY_COMMAND_TOPIC), KEY_STATE_TOPIC: valve_options.get(KEY_STATE_TOPIC), KEY_VALUE_TEMPLATE: valve_options.get(KEY_VALUE_TEMPLATE), KEY_PAYLOAD_OPEN: valve_options.get(KEY_PAYLOAD_OPEN), KEY_PAYLOAD_CLOSE: valve_options.get(KEY_PAYLOAD_CLOSE), KEY_REPORTS_POSITION: str( valve_options.get(KEY_REPORTS_POSITION, False) ).lower(), KEY_ENABLED_BY_DEFAULT: str(sensor_options[KEY_ENABLED_BY_DEFAULT]).lower(), KEY_AVAILABILITY: availability, KEY_UNIQUE_ID: f"{dev_id}-{valve}".lower(), KEY_QOS: qos, KEY_DEVICE: device_info, KEY_ORIGIN: origin_info, "~": default_topic, } if valve_options.get(KEY_DEVICE_CLASS): payload[KEY_DEVICE_CLASS] = valve_options[KEY_DEVICE_CLASS] if model == MODEL_SHELLYGAS and not device_config.get(CONF_VALVE_CONNECTED, False): payload = "" if not valve_options: payload = "" if dev_id.lower() in ignored: payload = "" mqtt_publish(config_topic, payload, retain)