1062 lines
52 KiB
Python
1062 lines
52 KiB
Python
import datetime
|
|
from dateutil import tz
|
|
import dateutil.parser as dp
|
|
import time
|
|
|
|
import apis
|
|
|
|
from theme import get_screensaver_color_output
|
|
from icon_mapping import get_icon_id
|
|
from icons import get_icon, get_icon_ha
|
|
from icons import get_action_icon
|
|
from helper import scale, rgb_dec565, rgb_brightness, get_attr_safe, convert_temperature
|
|
from localization import get_translation
|
|
from config import Entity
|
|
|
|
# check Babel
|
|
import importlib
|
|
babel_spec = importlib.util.find_spec("babel")
|
|
if babel_spec is not None:
|
|
import babel.dates
|
|
|
|
class LuiPagesGen(object):
|
|
|
|
def __init__(self, config, send_mqtt_msg):
|
|
self._config = config
|
|
self._locale = config.get("locale")
|
|
self._send_mqtt_msg = send_mqtt_msg
|
|
|
|
def get_entity_color(self, entity, ha_type=None, stateOverwrite=None, overwrite=None):
|
|
if overwrite is not None:
|
|
if type(overwrite) in [str, list]:
|
|
return rgb_dec565(overwrite)
|
|
if type(overwrite) is dict:
|
|
state = entity.state
|
|
for overwrite_state, overwrite_val in overwrite.items():
|
|
if overwrite_state == state:
|
|
return rgb_dec565(overwrite_val)
|
|
if isinstance(entity, str):
|
|
default_color = rgb_dec565([68, 115, 158])
|
|
return default_color
|
|
else:
|
|
state = entity.state
|
|
if stateOverwrite is not None:
|
|
state = stateOverwrite
|
|
attr = entity.attributes
|
|
default_color_on = rgb_dec565([253, 216, 53])
|
|
default_color_off = rgb_dec565([68, 115, 158])
|
|
icon_color = default_color_on if state in ["on", "unlocked", "above_horizon", "home", "active"] else default_color_off
|
|
|
|
if ha_type == "alarm_control_panel":
|
|
if state == "disarmed":
|
|
icon_color = rgb_dec565([13,160,53])
|
|
if state == "arming":
|
|
icon_color = rgb_dec565([244,180,0])
|
|
if state in ["armed_home", "armed_away", "armed_night", "armed_vacation", "pending", "triggered"]:
|
|
icon_color = rgb_dec565([223,76,30])
|
|
|
|
if ha_type == "climate":
|
|
if state in ["auto", "heat_cool"]:
|
|
icon_color = 1024
|
|
if state == "heat":
|
|
icon_color = 64512
|
|
if state == "off":
|
|
icon_color = 35921
|
|
if state == "cool":
|
|
icon_color = 11487
|
|
if state == "dry":
|
|
icon_color = 60897
|
|
if state == "fan_only":
|
|
icon_color = 35921
|
|
|
|
if ha_type == "weather":
|
|
if state in ["partlycloudy", "windy"]:
|
|
icon_color = 38066 #50% grey
|
|
if state == "clear-night":
|
|
icon_color = 38060 #yellow grey
|
|
if state == "windy-variant":
|
|
icon_color: 64495 #red grey
|
|
if state == "cloudy":
|
|
icon_color = 31728 #grey-blue
|
|
if state == "exceptional":
|
|
icon_color = 63878 #red
|
|
if state == "fog":
|
|
icon_color = 38066 #75% grey
|
|
if state in ["hail", "snowy"]:
|
|
icon_color = 65535 #white
|
|
if state == "lightning":
|
|
icon_color = 65120 #golden-yellow
|
|
if state == "lightning-rainy":
|
|
icon_color = 50400 #dark-golden-yellow
|
|
if state == "pouring":
|
|
icon_color = 12703 #blue
|
|
if state == "rainy":
|
|
icon_color = 25375 #light-blue
|
|
if state == "snowy-rainy":
|
|
icon_color = 38079 #light-blue-grey
|
|
if state == "sunny":
|
|
icon_color = 65504 #bright-yellow
|
|
|
|
if "rgb_color" in attr and attr.rgb_color:
|
|
color = attr.rgb_color
|
|
if "brightness" in attr and attr.brightness:
|
|
color = rgb_brightness(color, attr.brightness)
|
|
icon_color = rgb_dec565(color)
|
|
elif "brightness" in attr and attr.brightness:
|
|
color = rgb_brightness([253, 216, 53], attr.brightness)
|
|
icon_color = rgb_dec565(color)
|
|
return icon_color
|
|
|
|
|
|
def update_time(self, kwargs):
|
|
time = None
|
|
# get current time, with timezone if set
|
|
if self._config.get("timezone"):
|
|
timezone = tz.gettz(self._config.get("timezone"))
|
|
time = datetime.datetime.now(tz=timezone)
|
|
else:
|
|
time = datetime.datetime.now()
|
|
nice_time = time.strftime(self._config.get("timeFormat"))
|
|
addTemplate = self._config.get("timeAdditionalTemplate")
|
|
addTimeText = apis.ha_api.render_template(addTemplate)
|
|
self._send_mqtt_msg(f"time~{nice_time}~{addTimeText}")
|
|
|
|
def update_date(self, kwargs):
|
|
global babel_spec
|
|
if babel_spec is not None:
|
|
dateformat = self._config.get("dateFormatBabel")
|
|
date = babel.dates.format_date(datetime.datetime.now(), dateformat, locale=self._locale)
|
|
else:
|
|
dateformat = self._config.get("dateFormat")
|
|
date = datetime.datetime.now().strftime(dateformat)
|
|
|
|
addTemplate = self._config.get("dateAdditionalTemplate")
|
|
addDateText = apis.ha_api.render_template(addTemplate)
|
|
self._send_mqtt_msg(f"date~{date}{addDateText}")
|
|
|
|
def page_type(self, target_page):
|
|
if target_page == "cardUnlock":
|
|
target_page = "cardAlarm"
|
|
self._send_mqtt_msg(f"pageType~{target_page}")
|
|
|
|
def update_screensaver_weather(self, theme):
|
|
entities = self._config._config_screensaver.entities
|
|
|
|
# default screensaver based on configured entity
|
|
if len(entities) == 0:
|
|
entities.append(self._config._config_screensaver.entity)
|
|
for i in range(0,4):
|
|
entities.append(Entity({'entity': f'{self._config._config_screensaver.entity.entityId}','type': i}))
|
|
|
|
item_str = ""
|
|
for item in entities:
|
|
item_str += self.generate_entities_item(item, "cardEntities", mask=["type", "entityId"])
|
|
|
|
self._send_mqtt_msg(f"weatherUpdate{item_str}")
|
|
# send color if configured in screensaver
|
|
if theme is not None:
|
|
self._send_mqtt_msg(get_screensaver_color_output(theme=theme))
|
|
|
|
def update_status_icons(self):
|
|
status_res = ""
|
|
altfont = ""
|
|
for i in range(1,3):
|
|
statusIcon = self._config._config_screensaver.raw_config.get(f"statusIcon{i}")
|
|
if statusIcon is not None and apis.ha_api.entity_exists(statusIcon.get("entity","")):
|
|
icon = statusIcon.get("icon")
|
|
entity = apis.ha_api.get_entity(statusIcon.get("entity"))
|
|
entityType = statusIcon.get("entity").split(".")[0]
|
|
icon = get_icon_ha(statusIcon.get("entity"), overwrite=icon)
|
|
color = self.get_entity_color(entity, ha_type=entityType, overwrite=statusIcon.get("color", None))
|
|
status_res += f"~{icon}~{color}"
|
|
altfont += f'~{statusIcon.get("altFont", "")}'
|
|
else:
|
|
status_res += "~~"
|
|
altfont += "~"
|
|
self._send_mqtt_msg(f"statusUpdate{status_res}{altfont}")
|
|
|
|
def generate_entities_item(self, item, cardType="cardGrid", temp_unit="", mask=None):
|
|
entityId = item.entityId
|
|
icon = item.iconOverride
|
|
colorOverride = item.colorOverride
|
|
name = item.nameOverride
|
|
uuid = item.uuid
|
|
|
|
# check ha template for name
|
|
if item.nameOverride is not None and ("{" in item.nameOverride and "}" in item.nameOverride):
|
|
name = apis.ha_api.render_template(item.nameOverride)
|
|
|
|
# type of the item is the string before the "." in the entityId
|
|
if entityId is not None:
|
|
entityType = entityId.split(".")[0]
|
|
else:
|
|
entityType = "delete"
|
|
|
|
if entityId in ["sensor.weather_forecast_daily", "sensor.weather_forecast_hourly"]:
|
|
entityType = "weather"
|
|
|
|
apis.ha_api.log(f"Generating item for {entityId} with type {entityType}", level="DEBUG")
|
|
|
|
status_entity = apis.ha_api.get_entity(item.status) if item.status and apis.ha_api.entity_exists(item.status) else None
|
|
status_state = status_entity.state if status_entity is not None else None
|
|
|
|
entity = apis.ha_api.get_entity(entityId) if apis.ha_api.entity_exists(entityId) else None
|
|
entity_state = entity.state if entity is not None else None
|
|
|
|
state = status_state if status_state is not None else entity_state
|
|
|
|
if state is not None:
|
|
if item.condState is not None and item.condState != state:
|
|
return ""
|
|
if item.condStateNot is not None and item.condStateNot == state:
|
|
return ""
|
|
if item.condTemplate is not None and apis.ha_api.render_template(item.condTemplate):
|
|
return ""
|
|
|
|
# Internal types
|
|
if entityType == "delete":
|
|
return f"~{entityType}~~~~~"
|
|
if entityType == "navigate":
|
|
page_search_res = self._config.search_card(entityId)
|
|
if page_search_res is not None:
|
|
name = name if name is not None else page_search_res.title
|
|
text = get_translation(self._locale, "frontend.ui.card.button.press")
|
|
if status_entity:
|
|
icon_res = get_icon_ha(item.status, overwrite=icon)
|
|
icon_color = self.get_entity_color(status_entity, ha_type=item.status.split(".")[0], overwrite=colorOverride)
|
|
if item.status.startswith("sensor") and cardType in ["cardGrid", "cardGrid1", "cardGrid2"] and item.iconOverride is None:
|
|
icon_res = status_entity.state[:4]
|
|
if icon_res[-1] == ".":
|
|
icon_res = icon_res[:-1]
|
|
else:
|
|
#icon_color = rgb_dec565(colorOverride) if colorOverride is not None and type(colorOverride) is list else 17299
|
|
icon_color = self.get_entity_color(entityId, overwrite=colorOverride)
|
|
icon_res = get_icon_ha(entityId, overwrite=icon)
|
|
return f"~button~{entityId}~{icon_res}~{icon_color}~{name}~{text}"
|
|
else:
|
|
return f"~text~{entityId}~{get_icon_id('alert-circle-outline')}~17299~page not found~"
|
|
if entityType == "iText":
|
|
value = entityId.split(".", 2)[1]
|
|
name = name if name is not None else "conf name missing"
|
|
icon_color = icon_color = rgb_dec565(colorOverride) if colorOverride is not None and type(colorOverride) is list else 17299
|
|
icon_res = get_icon_id(icon) if icon is not None else get_icon_id("alert-circle-outline")
|
|
return f"~text~{entityId}~{icon_res}~{icon_color}~{name}~{value}"
|
|
if entityType == "service":
|
|
icon_id = get_icon("script", overwrite=icon)
|
|
text = get_translation(self._locale, "frontend.ui.card.script.run")
|
|
icon_color = icon_color = rgb_dec565(colorOverride) if colorOverride is not None and type(colorOverride) is list else 17299
|
|
if status_entity:
|
|
icon_id = get_icon_ha(item.status, overwrite=icon)
|
|
icon_color = self.get_entity_color(status_entity, ha_type=item.status.split(".")[0], overwrite=colorOverride)
|
|
if item.status.startswith("sensor") and cardType in ["cardGrid", "cardGrid1", "cardGrid2"] and item.iconOverride is None:
|
|
icon_id = status_entity.state[:4]
|
|
if icon_id[-1] == ".":
|
|
icon_id = icon_id[:-1]
|
|
return f"~button~{uuid}~{icon_id}~{icon_color}~{name}~{text}"
|
|
|
|
if entity is None:
|
|
return f"~text~{entityId}~{get_icon_id('alert-circle-outline')}~17299~Not found check~ apps.yaml"
|
|
|
|
|
|
# HA Entities
|
|
# common res vars
|
|
entityTypePanel = "text"
|
|
icon_id = get_icon_ha(entityId, overwrite=icon)
|
|
color = self.get_entity_color(entity, ha_type=entityType, overwrite=colorOverride)
|
|
value = ""
|
|
name = name if name is not None else entity.attributes.get("friendly_name","unknown")
|
|
|
|
if entityType == "cover":
|
|
entityTypePanel = "shutter"
|
|
device_class = entity.attributes.get("device_class", "window")
|
|
icon_id = get_icon_ha(entityId, overwrite=icon)
|
|
icon_up = ""
|
|
icon_stop = ""
|
|
icon_down = ""
|
|
icon_up_status = "disable"
|
|
icon_stop_status = "disable"
|
|
icon_down_status = "disable"
|
|
bits = entity.attributes.supported_features
|
|
pos = entity.attributes.get("current_position")
|
|
if pos is None:
|
|
pos_status = entity.state
|
|
pos = "disable"
|
|
else:
|
|
pos_status = pos
|
|
if bits & 0b00000001: # SUPPORT_OPEN
|
|
if ( pos != 100 and not (entity.state == "open" and pos == "disable") ) or item.assumedState:
|
|
icon_up_status = "enable"
|
|
icon_up = get_action_icon(ha_type=entityType, action="open", device_class=device_class)
|
|
if bits & 0b00000010: # SUPPORT_CLOSE
|
|
if ( pos != 0 and not (entity.state == "closed" and pos == "disable") ) or item.assumedState:
|
|
icon_down_status = "enable"
|
|
icon_down = get_action_icon(ha_type=entityType, action="close", device_class=device_class)
|
|
if bits & 0b00001000: # SUPPORT_STOP
|
|
icon_stop = get_action_icon(ha_type=entityType, action="stop", device_class=device_class)
|
|
icon_stop_status = "enable"
|
|
value = f"{icon_up}|{icon_stop}|{icon_down}|{icon_up_status}|{icon_stop_status}|{icon_down_status}"
|
|
elif entityType in "light":
|
|
entityTypePanel = "light"
|
|
value = 1 if entity.state == "on" else 0
|
|
elif entityType in ["switch", "input_boolean", "automation"]:
|
|
entityTypePanel = "switch"
|
|
value = 1 if entity.state == "on" else 0
|
|
elif entityType in "fan":
|
|
entityTypePanel = "fan"
|
|
value = 1 if entity.state == "on" else 0
|
|
elif entityType in ["sensor", "binary_sensor"]:
|
|
entityTypePanel = "text"
|
|
device_class = entity.attributes.get("device_class", "")
|
|
unit_of_measurement = entity.attributes.get("unit_of_measurement", "")
|
|
value = entity.state
|
|
|
|
# limit value to 4 chars on us-p
|
|
if self._config.get("model") == "us-p" and cardType == "cardEntities":
|
|
value = entity.state[:4]
|
|
if value[-1] == ".":
|
|
value = value[:-1]
|
|
|
|
if device_class != "temperature":
|
|
value = value + " "
|
|
value = value + unit_of_measurement
|
|
if entityType == "binary_sensor":
|
|
value = get_translation(self._locale, f"backend.component.binary_sensor.state.{device_class}.{entity.state}")
|
|
if cardType in ["cardGrid", "cardGrid1", "cardGrid2"] and entityType == "sensor" and icon is None:
|
|
icon_id = entity.state[:4]
|
|
if icon_id[-1] == ".":
|
|
icon_id = icon_id[:-1]
|
|
else:
|
|
icon_id = get_icon_ha(entityId, overwrite=icon)
|
|
elif entityType in ["button", "input_button"]:
|
|
entityTypePanel = "button"
|
|
value = get_translation(self._locale, "frontend.ui.card.button.press")
|
|
elif entityType == "scene":
|
|
entityTypePanel = "button"
|
|
value = get_translation(self._locale, "frontend.ui.card.scene.activate")
|
|
elif entityType == "script":
|
|
entityTypePanel = "button"
|
|
value = get_translation(self._locale, "frontend.ui.card.script.run")
|
|
elif entityType == "lock":
|
|
entityTypePanel = "button"
|
|
value = get_translation(self._locale, "frontend.ui.card.lock.lock") if entity.state == "unlocked" else get_translation(self._locale, "frontend.ui.card.lock.unlock")
|
|
elif entityType in ["number", "input_number"]:
|
|
entityTypePanel = "number"
|
|
min_v = entity.attributes.get("min", 0)
|
|
max_v = entity.attributes.get("max", 100)
|
|
value = f"{entity.state}|{min_v}|{max_v}"
|
|
elif entityType == "input_text":
|
|
entityTypePanel = "text"
|
|
value = entity.state
|
|
elif entityType in ["input_select", "select"]:
|
|
entityTypePanel = "input_sel"
|
|
value = entity.state
|
|
elif entityType == "vacuum":
|
|
entityTypePanel = "button"
|
|
if entity.state == "docked":
|
|
value = get_translation(self._locale, f"frontend.ui.card.vacuum.actions.start_cleaning")
|
|
else:
|
|
value = get_translation(self._locale, f"frontend.ui.card.vacuum.actions.return_to_base")
|
|
elif entityType == "alarm_control_panel":
|
|
entityTypePanel = "text"
|
|
value = get_translation(self._locale, f"frontend.state_badge.alarm_control_panel.{entity.state}")
|
|
elif entityType == "media_player":
|
|
entityTypePanel = "media_pl"
|
|
value = entity.state
|
|
elif entityType == "sun":
|
|
entityTypePanel = "text"
|
|
value = get_translation(self._locale, f"backend.component.sun.state._.{entity.state}")
|
|
elif entityType == "person":
|
|
entityTypePanel = "text"
|
|
value = get_translation(self._locale, f"backend.component.person.state._.{entity.state}")
|
|
elif entityType == "climate":
|
|
entityTypePanel = "text"
|
|
state_value = get_translation(self._locale, f"backend.component.climate.state._.{entity.state}")
|
|
temperature = get_attr_safe(entity, "temperature", "")
|
|
temperature_unit = "°C" if(temp_unit == "celsius") else "°F"
|
|
value = f"{state_value} {temperature}{temperature_unit}"
|
|
currently_tanslation = get_translation(self._locale, f"frontend.ui.card.climate.currently")
|
|
current_temperature = get_attr_safe(entity, "current_temperature", "")
|
|
value += f"\r\n{currently_tanslation}: {current_temperature}{temperature_unit}"
|
|
elif entityType == "timer":
|
|
entityTypePanel = "timer"
|
|
value = get_translation(self._locale, f"backend.component.timer.state._.{entity.state}")
|
|
elif entityType == "weather":
|
|
entityTypePanel = "text"
|
|
unit = get_attr_safe(entity, "temperature_unit", "")
|
|
if type(item.stype) == int and len(entity.attributes.forecast) >= item.stype:
|
|
fdate = dp.parse(entity.attributes.forecast[item.stype]['datetime'])
|
|
global babel_spec
|
|
if babel_spec is not None:
|
|
dateformat = "E" if item.nameOverride is None else item.nameOverride
|
|
name = babel.dates.format_datetime(fdate.astimezone(), dateformat, locale=self._locale)
|
|
else:
|
|
dateformat = "%a" if item.nameOverride is None else item.nameOverride
|
|
name = fdate.astimezone().strftime(dateformat)
|
|
icon_id = get_icon_ha(entityId, stateOverwrite=entity.attributes.forecast[item.stype]['condition'])
|
|
value = f'{entity.attributes.forecast[item.stype].get("temperature", "")}{unit}'
|
|
color = self.get_entity_color(entity, ha_type=entityType, stateOverwrite=entity.attributes.forecast[item.stype]['condition'], overwrite=colorOverride)
|
|
else:
|
|
value = f'{get_attr_safe(entity, "temperature", "")}{unit}'
|
|
else:
|
|
name = "unsupported"
|
|
# Overwrite for value
|
|
ovalue = item.value
|
|
if ovalue is not None:
|
|
splitted_string = ovalue.rpartition('}')
|
|
template_string = f"{splitted_string[0]}{splitted_string[1]}"
|
|
templates_result = apis.ha_api.render_template(template_string)
|
|
value = f"{templates_result}{splitted_string[2]}"
|
|
if self._locale == "he_IL" and any("\u0590" <= c <= "\u05EA" for c in name):
|
|
name = name[::-1]
|
|
# use uuid instead for some types and probably expand on this in future
|
|
if entityType in ["light"]:
|
|
entityId = uuid
|
|
# remove stuff defined in mask
|
|
if mask is not None:
|
|
if "type" in mask:
|
|
entityTypePanel = ""
|
|
if "entityId" in mask:
|
|
entityId = ""
|
|
# change font on cardgrid
|
|
if item.font:
|
|
font = 0
|
|
if item.font == "small":
|
|
font = 0
|
|
elif item.font == "medium-icon":
|
|
font = 1
|
|
elif item.font == "medium":
|
|
font = 2
|
|
elif item.font == "large":
|
|
font = 3
|
|
elif isinstance(item.font, int):
|
|
font = item.font
|
|
icon_id = f'{icon_id}¬{font}'
|
|
return f"~{entityTypePanel}~{entityId}~{icon_id}~{color}~{name}~{value}"
|
|
|
|
def generate_entities_page(self, navigation, heading, items, cardType, tempUnit):
|
|
command = f"entityUpd~{heading}~{navigation}"
|
|
# Get items and construct cmd string
|
|
for item in items:
|
|
command += self.generate_entities_item(item, cardType, tempUnit)
|
|
self._send_mqtt_msg(command)
|
|
|
|
def generate_thermo_page(self, navigation, title, entity, temp_unit, overwrite_supported_modes):
|
|
item = entity.entityId
|
|
|
|
if(temp_unit == "celsius"):
|
|
temperature_unit_icon = get_icon_id("temperature-celsius")
|
|
temperature_unit = "°C"
|
|
|
|
else:
|
|
temperature_unit_icon = get_icon_id("temperature-fahrenheit")
|
|
temperature_unit = "°F"
|
|
|
|
if not apis.ha_api.entity_exists(item):
|
|
command = f"entityUpd~Not found~{navigation}~{item}~check~220~apps.yaml~150~300~5~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Please~your~~"
|
|
else:
|
|
entity = apis.ha_api.get_entity(item)
|
|
heading = title if title != "unknown" else entity.attributes.friendly_name
|
|
current_temp = get_attr_safe(entity, "current_temperature", "")
|
|
dest_temp = get_attr_safe(entity, "temperature", None)
|
|
dest_temp2 = ""
|
|
if dest_temp is None:
|
|
dest_temp = get_attr_safe(entity, "target_temp_high", 0)
|
|
dest_temp2 = get_attr_safe(entity, "target_temp_low", None)
|
|
if dest_temp2 != None and dest_temp2 != "null":
|
|
dest_temp2 = int(dest_temp2*10)
|
|
else:
|
|
dest_temp2 = ""
|
|
dest_temp = int(dest_temp*10)
|
|
|
|
hvac_action = get_attr_safe(entity, "hvac_action", "")
|
|
state_value = ""
|
|
if hvac_action != "":
|
|
state_value = get_translation(self._locale, f"frontend.state_attributes.climate.hvac_action.{hvac_action}")
|
|
state_value += "\r\n("
|
|
state_value += get_translation(self._locale, f"backend.component.climate.state._.{entity.state}")
|
|
if hvac_action != "":
|
|
state_value += ")"
|
|
|
|
min_temp = int(get_attr_safe(entity, "min_temp", 0)*10)
|
|
max_temp = int(get_attr_safe(entity, "max_temp", 0)*10)
|
|
step_temp = int(get_attr_safe(entity, "target_temp_step", 0.5)*10)
|
|
icon_res_list = []
|
|
icon_res = ""
|
|
|
|
hvac_modes = get_attr_safe(entity, "hvac_modes", [])
|
|
if overwrite_supported_modes is not None:
|
|
hvac_modes = overwrite_supported_modes
|
|
for mode in hvac_modes:
|
|
icon_id = get_icon_ha(item, stateOverwrite=mode)
|
|
color_on = 64512
|
|
if mode in ["auto", "heat_cool"]:
|
|
color_on = 1024
|
|
if mode == "heat":
|
|
color_on = 64512
|
|
if mode == "off":
|
|
color_on = 35921
|
|
if mode == "cool":
|
|
color_on = 11487
|
|
if mode == "dry":
|
|
color_on = 60897
|
|
if mode == "fan_only":
|
|
color_on = 35921
|
|
state = 0
|
|
if(mode == entity.state):
|
|
state = 1
|
|
|
|
icon_res_list.append(f"~{icon_id}~{color_on}~{state}~{mode}")
|
|
|
|
icon_res = "".join(icon_res_list)
|
|
|
|
if len(icon_res_list) == 1 and not self._config.get("model") == "us-p":
|
|
icon_res = "~"*4 + icon_res_list[0] + "~"*4*6
|
|
elif len(icon_res_list) == 2 and not self._config.get("model") == "us-p":
|
|
icon_res = "~"*4*2 + icon_res_list[0] + "~"*4*2 + icon_res_list[1] + "~"*4*2
|
|
elif len(icon_res_list) == 3 and not self._config.get("model") == "us-p":
|
|
icon_res = "~"*4*2 + icon_res_list[0] + "~"*4 + icon_res_list[1] + "~"*4 + icon_res_list[2] + "~"*4
|
|
elif len(icon_res_list) == 4 and not self._config.get("model") == "us-p":
|
|
icon_res = "~"*4 + icon_res_list[0] + "~"*4 + icon_res_list[1] + "~"*4 + icon_res_list[2] + "~"*4 + icon_res_list[3]
|
|
elif len(icon_res_list) >= 5 or self._config.get("model") == "us-p":
|
|
icon_res = "".join(icon_res_list) + "~"*4*(8-len(icon_res_list))
|
|
|
|
currently_translation = get_translation(self._locale, "frontend.ui.card.climate.currently")
|
|
state_translation = get_translation(self._locale, "frontend.ui.panel.config.devices.entities.state")
|
|
action_translation = get_translation(self._locale, "frontend.ui.card.climate.operation").replace(' ','\r\n')
|
|
|
|
detailPage = "1"
|
|
if any(x in ["preset_modes", "swing_modes", "fan_modes"] for x in entity.attributes):
|
|
detailPage = "0"
|
|
|
|
command = f"entityUpd~{heading}~{navigation}~{item}~{current_temp} {temperature_unit}~{dest_temp}~{state_value}~{min_temp}~{max_temp}~{step_temp}{icon_res}~{currently_translation}~{state_translation}~{action_translation}~{temperature_unit_icon}~{dest_temp2}~{detailPage}"
|
|
self._send_mqtt_msg(command)
|
|
|
|
def generate_chart_page(self, navigation, title, entity):
|
|
item = entity.entityId
|
|
if not apis.ha_api.entity_exists(item):
|
|
command = f"entityUpd~Not found~{navigation}"
|
|
else:
|
|
entity = apis.ha_api.get_entity(item)
|
|
heading = title if title != "unknown" else entity.attributes.friendly_name
|
|
|
|
# get data from homeassistant
|
|
data_raw = apis.ha_api.get_history(entity_id = item, days = 7)
|
|
data = [(d.get('last_updated', None),d.get('state', None)) for d in data_raw[0]]
|
|
data = dict(data)
|
|
|
|
# Parse timestamps and convert to datetime objects, excluding 'unavailable' values
|
|
time_temp_pairs = [(datetime.datetime.fromisoformat(timestamp), int(val)) for timestamp, val in data.items() if val != 'unavailable']
|
|
# Sort the data based on timestamps
|
|
time_temp_pairs.sort(key=lambda x: x[0])
|
|
# Calculate the time span
|
|
start_time = time_temp_pairs[0][0]
|
|
end_time = time_temp_pairs[-1][0]
|
|
time_span = end_time - start_time
|
|
# Calculate time intervals for evenly spaced data points
|
|
num_data_points = 24
|
|
time_intervals = [start_time + i * time_span / (num_data_points - 1) for i in range(num_data_points)]
|
|
# Find the closest data points for these time intervals
|
|
evenly_spaced_data = []
|
|
for interval in time_intervals:
|
|
closest_pair = min(time_temp_pairs, key=lambda x: abs(x[0] - interval))
|
|
time, val = closest_pair
|
|
val = int(val)
|
|
evenly_spaced_data.append((time, val))
|
|
|
|
datapoints = ""
|
|
for idx, (time, val) in enumerate(evenly_spaced_data):
|
|
datapoints += f"{val}~"
|
|
|
|
color = 65504
|
|
ydesc = "Akku [%]"
|
|
yscale = "25:50:75:100"
|
|
#datapoints = "19^22:00~17~12~8~7^2:00~6~6~5~5^6:00~5~15~19~12^10:00~17~24~18~12^14:00~13~13~13~15^18:00~25~28~26"
|
|
|
|
command = f"entityUpd~{heading}~{navigation}~{color}~{ydesc}~{yscale}~{datapoints}"
|
|
self._send_mqtt_msg(command)
|
|
|
|
def generate_media_page(self, navigation, title, entity, entities, mediaBtn):
|
|
entityId = entity.entityId
|
|
if entity.status is not None:
|
|
entityId = entity.status
|
|
if not apis.ha_api.entity_exists(entityId):
|
|
command = f"entityUpd~Not found~{navigation}~{entityId}~Please check your~~apps.yaml in AppDaemon~~0~{get_icon_id('alert-circle-outline')}~~"
|
|
else:
|
|
media_icon = self.generate_entities_item(entity, "cardGrid")
|
|
ha_entity = apis.ha_api.get_entity(entityId)
|
|
heading = title if title != "unknown" else ha_entity.attributes.friendly_name
|
|
title = get_attr_safe(ha_entity, "media_title", "")
|
|
author = get_attr_safe(ha_entity, "media_artist", "")
|
|
volume = int(get_attr_safe(ha_entity, "volume_level", 0)*100)
|
|
iconplaypause = get_icon_id("pause") if ha_entity.state == "playing" else get_icon_id("play")
|
|
bits = ha_entity.attributes.supported_features
|
|
onoffbutton = "disable"
|
|
if bits & 0b10000000:
|
|
if ha_entity.state == "off":
|
|
onoffbutton = 1374
|
|
else:
|
|
onoffbutton = rgb_dec565([255,152,0])
|
|
shuffleBtn = "disable"
|
|
if bits & 0b100000000000000:
|
|
shuffle = get_attr_safe(ha_entity, "shuffle", "")
|
|
if shuffle == False:
|
|
shuffleBtn = get_icon_id('shuffle-disabled')
|
|
elif shuffle == True:
|
|
shuffleBtn = get_icon_id('shuffle')
|
|
|
|
item_str = ""
|
|
for item in entities:
|
|
item_str += self.generate_entities_item(item, "cardGrid")
|
|
|
|
bck_override = entity.iconOverride
|
|
if entity.status is not None:
|
|
bck_entity = entity.entityId
|
|
entity.entityId = entity.status
|
|
|
|
entity.iconOverride = "mdi:speaker"
|
|
item_str += self.generate_entities_item(entity, "cardGrid")
|
|
|
|
entity.iconOverride = bck_override
|
|
if entity.status is not None:
|
|
entity.entityId = bck_entity
|
|
|
|
command = f"entityUpd~{heading}~{navigation}~{entityId}~{title}~~{author}~~{volume}~{iconplaypause}~{onoffbutton}~{shuffleBtn}{media_icon}{item_str}"
|
|
self._send_mqtt_msg(command)
|
|
|
|
def generate_alarm_page(self, navigation, title, entity, overwrite_supported_modes, alarmBtn):
|
|
item = entity.entityId
|
|
if not apis.ha_api.entity_exists(item):
|
|
command = f"entityUpd~Not found~{navigation}~{item}~Not found~Not found~Check your~Check your~apps.~apps.~yaml~yaml~0~~0"
|
|
else:
|
|
entity = apis.ha_api.get_entity(item)
|
|
icon = get_icon_id("shield-off")
|
|
color = rgb_dec565([255,255,255])
|
|
supported_modes = []
|
|
numpad = "enable"
|
|
if entity.state == "disarmed":
|
|
color = rgb_dec565([13,160,53])
|
|
icon = get_icon_id("shield-off")
|
|
if not entity.attributes.get("code_arm_required", False):
|
|
numpad = "disable"
|
|
if overwrite_supported_modes is None:
|
|
bits = entity.attributes.supported_features
|
|
if bits & 0b000001:
|
|
supported_modes.append("arm_home")
|
|
if bits & 0b000010:
|
|
supported_modes.append("arm_away")
|
|
if bits & 0b000100:
|
|
supported_modes.append("arm_night")
|
|
if bits & 0b100000:
|
|
supported_modes.append("arm_vacation")
|
|
else:
|
|
supported_modes = overwrite_supported_modes
|
|
else:
|
|
supported_modes.append("disarm")
|
|
|
|
if entity.state == "armed_home":
|
|
color = rgb_dec565([223,76,30])
|
|
icon = get_icon_id("shield-home")
|
|
if entity.state == "armed_away":
|
|
color = rgb_dec565([223,76,30])
|
|
icon = get_icon_id("shield-lock")
|
|
if entity.state == "armed_night":
|
|
color = rgb_dec565([223,76,30])
|
|
icon = get_icon_id("weather-night")
|
|
if entity.state == "armed_vacation":
|
|
color = rgb_dec565([223,76,30])
|
|
icon = get_icon_id("shield-airplane")
|
|
|
|
flashing = "disable"
|
|
if entity.state in ["arming", "pending"]:
|
|
color = rgb_dec565([243,179,0])
|
|
icon = get_icon_id("shield")
|
|
flashing = "enable"
|
|
if entity.state == "triggered":
|
|
color = rgb_dec565([223,76,30])
|
|
icon = get_icon_id("bell-ring")
|
|
flashing = "enable"
|
|
|
|
#add button to show sensor state
|
|
add_btn = ""
|
|
if "open_sensors" in entity.attributes and entity.attributes.open_sensors is not None:
|
|
add_btn=f"{get_icon_id('progress-alert')}~{rgb_dec565([243,179,0])}~"
|
|
if alarmBtn is not None and type(alarmBtn) is dict:
|
|
entity = alarmBtn.get("entity")
|
|
iconnav = get_icon("alarm-arm-fail", overwrite=alarmBtn.get("icon"))
|
|
status = alarmBtn.get("status")
|
|
if status is not None and apis.ha_api.entity_exists(status):
|
|
icon_color = self.get_entity_color(apis.ha_api.get_entity(status))
|
|
else:
|
|
icon_color = rgb_dec565([243,179,0])
|
|
add_btn=f"{iconnav}~{icon_color}~{entity}"
|
|
|
|
|
|
# add padding to arm buttons
|
|
arm_buttons = ""
|
|
for b in supported_modes:
|
|
modeName = f"frontend.ui.card.alarm_control_panel.{b}"
|
|
arm_buttons += f"~{get_translation(self._locale, modeName)}~{b}"
|
|
if len(supported_modes) < 4:
|
|
arm_buttons += "~"*((4-len(supported_modes))*2)
|
|
command = f"entityUpd~{title}~{navigation}~{item}{arm_buttons}~{icon}~{color}~{numpad}~{flashing}~{add_btn}"
|
|
self._send_mqtt_msg(command)
|
|
|
|
def generate_unlock_page(self, navigation, item, title, destination, pin):
|
|
color = rgb_dec565([255,0,0])
|
|
icon = get_icon_id("lock")
|
|
supported_modes = ["cardUnlock-unlock"]
|
|
|
|
# add padding to arm buttons
|
|
arm_buttons = ""
|
|
for b in supported_modes:
|
|
arm_buttons += f'~{get_translation(self._locale, "frontend.ui.card.lock.unlock")}~{b}'
|
|
if len(supported_modes) < 4:
|
|
arm_buttons += "~"*((4-len(supported_modes))*2)
|
|
numpad = "enable"
|
|
command = f"entityUpd~{title}~{navigation}~{item}{arm_buttons}~{icon}~{color}~{numpad}~disable~"
|
|
self._send_mqtt_msg(command)
|
|
|
|
def generate_qr_page(self, navigation, heading, items, cardType, qrcode):
|
|
qrcode = apis.ha_api.render_template(qrcode)
|
|
command = f"entityUpd~{heading}~{navigation}~{qrcode}"
|
|
# Get items and construct cmd string
|
|
for item in items:
|
|
command += self.generate_entities_item(item, cardType)
|
|
self._send_mqtt_msg(command)
|
|
|
|
def generate_power_page(self, navigation, heading, items):
|
|
command = f"entityUpd~{heading}~{navigation}"
|
|
for item in items:
|
|
command += self.generate_entities_item(item, "cardEntities")
|
|
speed = 0
|
|
if apis.ha_api.entity_exists(item.entityId):
|
|
entity = apis.ha_api.get_entity(item.entityId)
|
|
speed = str(item.entity_input_config.get("speed", 0))
|
|
if isinstance(speed, str):
|
|
speed = apis.ha_api.render_template(speed)
|
|
command += f"~{speed}"
|
|
self._send_mqtt_msg(command)
|
|
|
|
def render_card(self, card, send_page_type=True):
|
|
|
|
# page type is false, so this request is from a callback
|
|
if not send_page_type and card.cooldown != 0:
|
|
if (time.time()-card.last_update) < card.cooldown:
|
|
return
|
|
card.last_update = time.time()
|
|
|
|
|
|
leftBtn = "delete~~~~~"
|
|
if card.uuid_prev is not None:
|
|
leftBtn = self.generate_entities_item(Entity(
|
|
{
|
|
'entity': f'navigate.{card.uuid_prev}',
|
|
'icon': 'mdi:arrow-left-bold',
|
|
'color': [255, 255, 255],
|
|
}
|
|
))[1:]
|
|
|
|
rightBtn = "delete~~~~~"
|
|
if card.uuid_prev is not None:
|
|
rightBtn = self.generate_entities_item(Entity(
|
|
{
|
|
'entity': f'navigate.{card.uuid_next}',
|
|
'icon': 'mdi:arrow-right-bold',
|
|
'color': [255, 255, 255],
|
|
}
|
|
))[1:]
|
|
|
|
if card.hidden:
|
|
leftBtn = f"x~navUp~{get_icon_id('mdi:arrow-up-bold')}~65535~~"
|
|
rightBtn = "delete~~~~~"
|
|
|
|
if card.nav1Override is not None:
|
|
leftBtn = self.generate_entities_item(card.nav1Override)[1:]
|
|
|
|
if card.nav2Override is not None:
|
|
rightBtn = self.generate_entities_item(card.nav2Override)[1:]
|
|
|
|
navigation = f"{leftBtn}~{rightBtn}"
|
|
|
|
# Switch to page
|
|
if send_page_type:
|
|
if card.cardType == "cardGrid" and len(card.entities) > 6:
|
|
card.cardType = "cardGrid2"
|
|
if card.cardType == "cardGrid1":
|
|
card.cardType = "cardGrid"
|
|
self.page_type(card.cardType)
|
|
|
|
# send sleep timeout if there is one configured for the current card
|
|
if card.sleepTimeout is not None:
|
|
self._send_mqtt_msg(f"timeout~{card.sleepTimeout}")
|
|
else:
|
|
self._send_mqtt_msg(f'timeout~{self._config.get("sleepTimeout")}')
|
|
|
|
temp_unit = card.raw_config.get("temperatureUnit", "celsius")
|
|
if card.cardType in ["cardEntities", "cardGrid", "cardGrid1","cardGrid2"]:
|
|
self.generate_entities_page(navigation, card.title, card.entities, card.cardType, temp_unit)
|
|
return
|
|
if card.cardType == "cardThermo":
|
|
temp_unit = card.raw_config.get("temperatureUnit", "celsius")
|
|
overwrite_supported_modes = card.raw_config.get("supportedModes")
|
|
self.generate_thermo_page(navigation, card.title, card.entity, temp_unit, overwrite_supported_modes)
|
|
return
|
|
if card.cardType == "cardMedia":
|
|
mediaBtn = card.raw_config.get("mediaControl", "")
|
|
self.generate_media_page(navigation, card.title, card.entity, card.entities, mediaBtn)
|
|
return
|
|
if card.cardType == "cardAlarm":
|
|
alarmBtn = card.raw_config.get("alarmControl")
|
|
overwrite_supported_modes = card.raw_config.get("supportedModes")
|
|
self.generate_alarm_page(navigation, card.title, card.entity, overwrite_supported_modes, alarmBtn)
|
|
return
|
|
if card.cardType == "cardUnlock":
|
|
pin = card.raw_config.get("pin", 3830)
|
|
destination = card.raw_config.get("destination")
|
|
item = card.uuid
|
|
self.generate_unlock_page(navigation, item, card.title, destination, pin)
|
|
return
|
|
if card.cardType in ["screensaver", "screensaver2"]:
|
|
theme = card.raw_config.get("theme")
|
|
self.update_screensaver_weather(theme)
|
|
self.update_status_icons()
|
|
return
|
|
if card.cardType == "cardQR":
|
|
qrcode = card.raw_config.get("qrCode", "")
|
|
self.generate_qr_page(navigation, card.title, card.entities, card.cardType, qrcode)
|
|
return
|
|
if card.cardType == "cardPower":
|
|
self.generate_power_page(navigation, card.title, card.entities)
|
|
return
|
|
if card.cardType == "cardChart":
|
|
self.generate_chart_page(navigation, card.title, card.entity)
|
|
return
|
|
|
|
|
|
def generate_light_detail_page(self, entity_id, is_open_detail=False):
|
|
if entity_id.startswith('uuid'):
|
|
entity_config = self._config._config_entites_table.get(entity_id)
|
|
entity = apis.ha_api.get_entity(entity_config.entityId)
|
|
else:
|
|
entity = apis.ha_api.get_entity(entity_id)
|
|
switch_val = 1 if entity.state == "on" else 0
|
|
icon_color = self.get_entity_color(entity)
|
|
brightness = "disable"
|
|
color_temp = "disable"
|
|
color = "disable"
|
|
effect_supported = "disable"
|
|
|
|
if "onoff" not in entity.attributes.supported_color_modes:
|
|
brightness = 0
|
|
if entity.state == "on":
|
|
if "brightness" in entity.attributes and entity.attributes.brightness:
|
|
# scale 0-255 brightness from ha to 0-100
|
|
brightness = int(scale(entity.attributes.brightness,(0,255),(0,100)))
|
|
else:
|
|
brightness = "disable"
|
|
if "color_temp" in entity.attributes.supported_color_modes and entity.attributes.supported_color_modes:
|
|
if "color_temp" in entity.attributes and entity.attributes.color_temp:
|
|
# scale ha color temp range to 0-100
|
|
color_temp = int(scale(entity.attributes.color_temp,(entity.attributes.min_mireds, entity.attributes.max_mireds),(0,100)))
|
|
else:
|
|
color_temp = "unknown"
|
|
else:
|
|
color_temp = "disable"
|
|
list_color_modes = ["xy", "rgb", "rgbw", "hs"]
|
|
if any(item in list_color_modes for item in entity.attributes.supported_color_modes):
|
|
color = "enable"
|
|
else:
|
|
color = "disable"
|
|
if "effect_list" in entity.attributes:
|
|
effect_supported = "enable"
|
|
color_translation = "Color"
|
|
brightness_translation = get_translation(self._locale, "frontend.ui.card.light.brightness")
|
|
color_temp_translation = get_translation(self._locale, "frontend.ui.card.light.color_temperature")
|
|
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~~{icon_color}~{switch_val}~{brightness}~{color_temp}~{color}~{color_translation}~{color_temp_translation}~{brightness_translation}~{effect_supported}", force=is_open_detail)
|
|
|
|
def generate_shutter_detail_page(self, entity_id, is_open_detail=False):
|
|
entity = apis.ha_api.get_entity(entity_id)
|
|
entityType = "cover"
|
|
device_class = entity.attributes.get("device_class", "window")
|
|
icon_id = get_icon_ha(entity_id)
|
|
|
|
pos = entity.attributes.get("current_position")
|
|
if pos is None:
|
|
pos_status = entity.state
|
|
pos = "disable"
|
|
else:
|
|
pos_status = pos
|
|
|
|
pos_translation = ""
|
|
icon_up = ""
|
|
icon_stop = ""
|
|
icon_down = ""
|
|
icon_up_status = "disable"
|
|
icon_stop_status = "disable"
|
|
icon_down_status = "disable"
|
|
textTilt = ""
|
|
iconTiltLeft = ""
|
|
iconTiltStop = ""
|
|
iconTiltRight = ""
|
|
iconTiltLeftStatus = "disable"
|
|
iconTiltStopStatus = "disable"
|
|
iconTiltRightStatus = "disable"
|
|
tilt_pos = "disable"
|
|
|
|
bits = entity.attributes.supported_features
|
|
|
|
# position supported
|
|
if bits & 0b00001111:
|
|
pos_translation = get_translation(self._locale, "frontend.ui.card.cover.position")
|
|
if bits & 0b00000001: # SUPPORT_OPEN
|
|
if ( pos != 100 and not (entity.state == "open" and pos == "disable") ):
|
|
icon_up_status = "enable"
|
|
icon_up = get_action_icon(ha_type=entityType, action="open", device_class=device_class)
|
|
if bits & 0b00000010: # SUPPORT_CLOSE
|
|
if ( pos != 0 and not (entity.state == "closed" and pos == "disable") ):
|
|
icon_down_status = "enable"
|
|
icon_down = get_action_icon(ha_type=entityType, action="close", device_class=device_class)
|
|
#if bits & 0b00000100: # SUPPORT_SET_POSITION
|
|
if bits & 0b00001000: # SUPPORT_STOP
|
|
icon_stop = get_action_icon(ha_type=entityType, action="stop", device_class=device_class)
|
|
icon_stop_status = "enable"
|
|
|
|
# tilt supported
|
|
if bits & 0b11110000:
|
|
textTilt = get_translation(self._locale, "frontend.ui.card.cover.tilt_position")
|
|
if bits & 0b00010000: # SUPPORT_OPEN_TILT
|
|
iconTiltLeft = get_icon_id('arrow-top-right')
|
|
iconTiltLeftStatus = "enable"
|
|
if bits & 0b00100000: # SUPPORT_CLOSE_TILT
|
|
iconTiltRight = get_icon_id('arrow-bottom-left')
|
|
iconTiltRightStatus = "enable"
|
|
if bits & 0b01000000: # SUPPORT_STOP_TILT
|
|
iconTiltStop = get_icon_id('stop')
|
|
iconTiltStopStatus = "enable"
|
|
if bits & 0b10000000: # SUPPORT_SET_TILT_POSITION
|
|
tilt_pos = get_attr_safe(entity, "current_tilt_position", 0)
|
|
if(tilt_pos == 0):
|
|
iconTiltRightStatus = "disable"
|
|
if(tilt_pos == 100):
|
|
iconTiltLeftStatus = "disable"
|
|
|
|
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~{pos}~{pos_translation}: {pos_status}~{pos_translation}~{icon_id}~{icon_up}~{icon_stop}~{icon_down}~{icon_up_status}~{icon_stop_status}~{icon_down_status}~{textTilt}~{iconTiltLeft}~{iconTiltStop}~{iconTiltRight}~{iconTiltLeftStatus}~{iconTiltStopStatus}~{iconTiltRightStatus}~{tilt_pos}", force=is_open_detail)
|
|
|
|
def generate_fan_detail_page(self, entity_id, is_open_detail=False):
|
|
entity = apis.ha_api.get_entity(entity_id)
|
|
switch_val = 1 if entity.state == "on" else 0
|
|
icon_color = self.get_entity_color(entity)
|
|
speed = entity.attributes.get("percentage")
|
|
percentage_step = entity.attributes.get("percentage_step")
|
|
speedMax = 100
|
|
if percentage_step is None:
|
|
speed = "disable"
|
|
else:
|
|
if speed is None:
|
|
speed = 0
|
|
speed = round(speed/percentage_step)
|
|
speedMax = int(100/percentage_step)
|
|
|
|
speed_translation = get_translation(self._locale, "frontend.ui.card.fan.speed")
|
|
|
|
preset_mode = entity.attributes.get("preset_mode", "")
|
|
preset_modes = entity.attributes.get("preset_modes", [])
|
|
if preset_modes is not None:
|
|
preset_modes = "?".join(entity.attributes.get("preset_modes", []))
|
|
else:
|
|
preset_modes = ""
|
|
|
|
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~~{icon_color}~{switch_val}~{speed}~{speedMax}~{speed_translation}~{preset_mode}~{preset_modes}", force=is_open_detail)
|
|
|
|
def generate_thermo_detail_page(self, entity_id, is_open_detail=False):
|
|
icon_id = get_icon_ha(entity_id)
|
|
entity = apis.ha_api.get_entity(entity_id)
|
|
icon_color = self.get_entity_color(entity, ha_type="climate")
|
|
|
|
modes_out = ""
|
|
for mode in ["preset_modes", "swing_modes", "fan_modes"]:
|
|
heading = get_translation(self._locale, f"frontend.ui.card.climate.{mode[:-1]}")
|
|
cur_mode = entity.attributes.get(mode[:-1], "")
|
|
modes = entity.attributes.get(mode, [])
|
|
if modes is not None:
|
|
if mode == "preset_modes":
|
|
translated_modes = []
|
|
for elem in modes:
|
|
translated_modes.append(get_translation(self._locale, f"frontend.state_attributes.climate.preset_mode.{elem}"))
|
|
cur_mode = get_translation(self._locale, f"frontend.state_attributes.climate.preset_mode.{cur_mode}")
|
|
modes_res = "?".join(translated_modes)
|
|
else:
|
|
modes_res = "?".join(modes)
|
|
if modes:
|
|
modes_out += f"{heading}~{mode}~{cur_mode}~{modes_res}~"
|
|
|
|
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~{icon_id}~{icon_color}~{modes_out}", force=is_open_detail)
|
|
|
|
def generate_input_select_detail_page(self, entity_id, is_open_detail=False):
|
|
options_list = None
|
|
if entity_id.startswith('uuid'):
|
|
entity_config = self._config._config_entites_table.get(entity_id)
|
|
entity = apis.ha_api.get_entity(entity_config.entityId)
|
|
ha_type = entity_config.entityId.split(".")[0]
|
|
options_list = entity_config.entity_input_config.get("effectList")
|
|
else:
|
|
entity = apis.ha_api.get_entity(entity_id)
|
|
ha_type = entity_id.split(".")[0]
|
|
options = []
|
|
icon_color = 0
|
|
icon_color = self.get_entity_color(entity, ha_type=ha_type)
|
|
state = entity.state
|
|
if ha_type in ["input_select", "select"]:
|
|
options = entity.attributes.get("options", [])
|
|
elif ha_type == "light":
|
|
if options_list is not None:
|
|
options = options_list
|
|
else:
|
|
options = entity.attributes.get("effect_list", [])[:15]
|
|
elif ha_type == "media_player":
|
|
state = entity.attributes.get("source", "")
|
|
options = entity.attributes.get("source_list", [])
|
|
options = "?".join(options)
|
|
self._send_mqtt_msg(f"entityUpdateDetail2~{entity_id}~~{icon_color}~{ha_type}~{state}~{options}~", force=is_open_detail)
|
|
|
|
def generate_timer_detail_page(self, entity_id, is_open_detail=False):
|
|
if isinstance(entity_id, dict):
|
|
entity_id = entity_id["entity_id"]
|
|
entity = apis.ha_api.get_entity(entity_id)
|
|
icon_color = self.get_entity_color(entity)
|
|
if entity.state in ["idle", "paused"]:
|
|
editable = 1
|
|
if entity.state == "paused":
|
|
time_remaining = entity.attributes.get("remaining")
|
|
else:
|
|
time_remaining = entity.attributes.get("duration")
|
|
min_remaining = time_remaining.split(":")[1]
|
|
sec_remaining = time_remaining.split(":")[2]
|
|
action1 = ""
|
|
action2 = "start"
|
|
action3 = ""
|
|
label1 = ""
|
|
label2 = get_translation(self._locale, "frontend.ui.card.timer.actions.start")
|
|
label3 = ""
|
|
else: #active
|
|
editable = 0
|
|
apis.ha_api.run_in(self.generate_timer_detail_page, 1, entity_id=entity_id)
|
|
finishes_at = dp.parse(entity.attributes.get("finishes_at"))
|
|
delta = finishes_at - datetime.datetime.now(datetime.timezone.utc)
|
|
hours, remainder = divmod(delta.total_seconds(), 3600)
|
|
minutes, seconds = divmod(remainder, 60)
|
|
min_remaining = int(minutes)
|
|
sec_remaining = int(seconds)
|
|
action1 = "pause"
|
|
action2 = "cancel"
|
|
action3 = "finish"
|
|
label1 = get_translation(self._locale, "frontend.ui.card.timer.actions.pause")
|
|
label2 = get_translation(self._locale, "frontend.ui.card.timer.actions.cancel")
|
|
label3 = get_translation(self._locale, "frontend.ui.card.timer.actions.finish")
|
|
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~~{icon_color}~{entity_id}~{min_remaining}~{sec_remaining}~{editable}~{action1}~{action2}~{action3}~{label1}~{label2}~{label3}", force=is_open_detail)
|
|
|
|
def send_message_page(self, ident, heading, msg, b1, b2):
|
|
self._send_mqtt_msg(f"pageType~popupNotify")
|
|
self._send_mqtt_msg(f"entityUpdateDetail~{ident}~{heading}~65535~{b1}~65535~{b2}~65535~{msg}~65535~0")
|
|
|