Take Unavailable and the Activate, Run and Press words from Home Assistant's frontend too

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-19 14:40:27 +02:00
parent 8fb39a52a6
commit 851c1772e5
15 changed files with 288 additions and 222 deletions
+1 -1
View File
@@ -149,7 +149,7 @@ inline const char *idle_text(const std::string &state) {
if (state == "off") return screen_text::tr(screen_text::txt::ha_off); if (state == "off") return screen_text::tr(screen_text::txt::ha_off);
if (state == "standby") return screen_text::tr(screen_text::txt::ha_media_standby); if (state == "standby") return screen_text::tr(screen_text::txt::ha_media_standby);
if (state == "buffering") return screen_text::tr(screen_text::txt::media_loading); if (state == "buffering") return screen_text::tr(screen_text::txt::media_loading);
if (state == "unavailable") return screen_text::tr(screen_text::txt::tile_unavailable); if (state == "unavailable") return screen_text::tr(screen_text::txt::ha_unavailable);
return screen_text::tr(screen_text::txt::media_not_playing); return screen_text::tr(screen_text::txt::media_not_playing);
} }
inline bool playing(const std::string &state) { return state == "playing" || state == "buffering"; } inline bool playing(const std::string &state) { return state == "playing" || state == "buffering"; }
+6 -6
View File
@@ -920,7 +920,7 @@ inline std::string detail_state(const Tile &t){
if(t.state=="returning")return tr(txt::ha_vacuum_returning); if(t.state=="returning")return tr(txt::ha_vacuum_returning);
if(t.state=="idle")return tr(txt::ha_vacuum_idle); if(t.state=="idle")return tr(txt::ha_vacuum_idle);
if(t.state=="error")return tr(txt::vacuum_check_robot); if(t.state=="error")return tr(txt::vacuum_check_robot);
if(!t.available())return tr(txt::tile_unavailable); if(!t.available())return tr(txt::ha_unavailable);
// Home Assistant's word where the screen has none of its own (firmware 0.2.58+). // Home Assistant's word where the screen has none of its own (firmware 0.2.58+).
if(!t.extra().state_word.empty())return t.extra().state_word; if(!t.extra().state_word.empty())return t.extra().state_word;
return t.unit.empty()?t.state:screen_text::localize(t.state); return t.unit.empty()?t.state:screen_text::localize(t.state);
@@ -1131,7 +1131,7 @@ inline void render_weather_detail(const Tile &t,bool large,int width,int height,
detail_text(now,degrees(t.current),temp_x,cy+(hero-big_h)/2,temp_w,big,LV_TEXT_ALIGN_LEFT,ink); detail_text(now,degrees(t.current),temp_x,cy+(hero-big_h)/2,temp_w,big,LV_TEXT_ALIGN_LEFT,ink);
int text_x=temp_x+temp_w+(large?4:2),text_w=width-2*pad-card_pad-text_x; int text_x=temp_x+temp_w+(large?4:2),text_w=width-2*pad-card_pad-text_x;
int lines_h=text_h+small_h+(large?2:0); int lines_h=text_h+small_h+(large?2:0);
detail_text(now,t.available()?weather_text(t.state):tr(txt::tile_unavailable),text_x,cy+(hero-lines_h)/2,text_w,detail_font,LV_TEXT_ALIGN_LEFT,ink); detail_text(now,t.available()?weather_text(t.state):tr(txt::ha_unavailable),text_x,cy+(hero-lines_h)/2,text_w,detail_font,LV_TEXT_ALIGN_LEFT,ink);
std::string details; std::string details;
if(std::isfinite(weather.feels))details=fill(txt::weather_feels_like,"n",(int)std::lround(weather.feels)); if(std::isfinite(weather.feels))details=fill(txt::weather_feels_like,"n",(int)std::lround(weather.feels));
if(std::isfinite(t.humidity)){snprintf(b,sizeof(b),"%d%%",(int)std::lround(t.humidity));details+=(details.empty()?"":" · ")+std::string(b);} if(std::isfinite(t.humidity)){snprintf(b,sizeof(b),"%d%%",(int)std::lround(t.humidity));details+=(details.empty()?"":" · ")+std::string(b);}
@@ -1373,7 +1373,7 @@ inline constexpr uint32_t COVER_ACCENT = theme::ha::PURPLE;
inline uint32_t cover_track(){return theme::tint(COVER_ACCENT,37);} inline uint32_t cover_track(){return theme::tint(COVER_ACCENT,37);}
inline uint32_t cover_slats(){return theme::tint(COVER_ACCENT,80);} inline uint32_t cover_slats(){return theme::tint(COVER_ACCENT,80);}
inline lv_obj_t *cover_values[2]{}; inline lv_obj_t *cover_values[2]{};
inline std::string cover_status_line(const Tile &t){return t.available()?tile_controls::cover_card_status(t):tr(txt::tile_unavailable);} inline std::string cover_status_line(const Tile &t){return t.available()?tile_controls::cover_card_status(t):tr(txt::ha_unavailable);}
inline void cover_slider_event(lv_event_t *e){ inline void cover_slider_event(lv_event_t *e){
auto *slider=lv_event_get_target_obj(e);auto code=lv_event_get_code(e); auto *slider=lv_event_get_target_obj(e);auto code=lv_event_get_code(e);
const bool tilt=(uintptr_t)lv_event_get_user_data(e)==1; const bool tilt=(uintptr_t)lv_event_get_user_data(e)==1;
@@ -1570,7 +1570,7 @@ inline bool history_fits(const Tile &t){
// Home Assistant's word for the state now: from this entity's history words, else the card's own. The history can // Home Assistant's word for the state now: from this entity's history words, else the card's own. The history can
// still be the previous card's while this one waits for its answer. // still be the previous card's while this one waits for its answer.
inline std::string history_words(const Tile &t){ inline std::string history_words(const Tile &t){
if(!t.available())return tr(txt::tile_unavailable); if(!t.available())return tr(txt::ha_unavailable);
if(history.entity==t.entity)for(const auto &pair:history.words)if(pair.first==t.state)return pair.second; if(history.entity==t.entity)for(const auto &pair:history.words)if(pair.first==t.state)return pair.second;
return detail_state(t); return detail_state(t);
} }
@@ -1809,7 +1809,7 @@ inline void render_history_detail(const Tile &t,bool large,int width,int height,
if(line){ if(line){
int decimals=0;const auto dot=t.state.find('.');if(dot!=std::string::npos)decimals=static_cast<int>(std::min<size_t>(4,t.state.size()-dot-1)); int decimals=0;const auto dot=t.state.find('.');if(dot!=std::string::npos)decimals=static_cast<int>(std::min<size_t>(4,t.state.size()-dot-1));
if(c.ready)decimals=h.decimals; if(c.ready)decimals=h.decimals;
c.value_text=!t.available()?std::string(tr(txt::tile_unavailable)):numeric?history_view::number(current,decimals,t.unit):t.state; c.value_text=!t.available()?std::string(tr(txt::ha_unavailable)):numeric?history_view::number(current,decimals,t.unit):t.state;
if(c.ready){ if(c.ready){
// The value now counts too: it can lie beyond the history (the running hour is not in the statistics yet). // The value now counts too: it can lie beyond the history (the running hour is not in the statistics yet).
if(h.has_high){c.high=h.high;c.high_at=h.high_at;} if(h.has_high){c.high=h.high;c.high_at=h.high_at;}
@@ -3117,7 +3117,7 @@ inline void render_slot(size_t slot) {
// Nothing in Home Assistant stands behind the settings card, so it says the same with the link down. // Nothing in Home Assistant stands behind the settings card, so it says the same with the link down.
if (t.is_settings()) value = tr(txt::tile_tap_to_open); if (t.is_settings()) value = tr(txt::tile_tap_to_open);
else if (t.is_page()) value = fill(txt::tile_page, "n", t.page_target()); else if (t.is_page()) value = fill(txt::tile_page, "n", t.page_target());
else if (!fresh() || !t.available()) value = tr(txt::tile_unavailable); else if (!fresh() || !t.available()) value = tr(txt::ha_unavailable);
else if (t.refused_at && esphome::millis() - t.refused_at < 4000) value = tr(txt::tile_refused); else if (t.refused_at && esphome::millis() - t.refused_at < 4000) value = tr(txt::tile_refused);
else if (d == "light" && t.state == "on" && tile_controls::effect_running(t.extra().effect)) value = t.extra().effect; else if (d == "light" && t.state == "on" && tile_controls::effect_running(t.extra().effect)) value = t.extra().effect;
else if (d == "light" && t.state == "on" && std::isfinite(t.brightness)) value = std::to_string(static_cast<int>(std::lround(std::clamp(t.brightness, 0.0f, 255.0f) * 100 / 255))) + " %"; else if (d == "light" && t.state == "on" && std::isfinite(t.brightness)) value = std::to_string(static_cast<int>(std::lround(std::clamp(t.brightness, 0.0f, 255.0f) * 100 / 255))) + " %";
+194 -194
View File
@@ -73,199 +73,199 @@ constexpr uint16_t settings_hours = 108;
constexpr uint16_t settings_hours_minutes = 109; constexpr uint16_t settings_hours_minutes = 109;
constexpr uint16_t tile_tap_to_open = 110; constexpr uint16_t tile_tap_to_open = 110;
constexpr uint16_t tile_page = 111; constexpr uint16_t tile_page = 111;
constexpr uint16_t tile_unavailable = 112; constexpr uint16_t tile_refused = 112;
constexpr uint16_t tile_refused = 113; constexpr uint16_t tile_command_sent = 113;
constexpr uint16_t tile_command_sent = 114; constexpr uint16_t tile_confirmed = 114;
constexpr uint16_t tile_confirmed = 115; constexpr uint16_t alert_notification = 115;
constexpr uint16_t alert_notification = 116; constexpr uint16_t alert_ok = 116;
constexpr uint16_t alert_ok = 117; constexpr uint16_t light_color = 117;
constexpr uint16_t light_color = 118; constexpr uint16_t light_color_temperature = 118;
constexpr uint16_t light_color_temperature = 119; constexpr uint16_t light_brightness = 119;
constexpr uint16_t light_brightness = 120; constexpr uint16_t light_waiting = 120;
constexpr uint16_t light_waiting = 121; constexpr uint16_t effects_effect = 121;
constexpr uint16_t effects_effect = 122; constexpr uint16_t effects_nothing = 122;
constexpr uint16_t effects_nothing = 123; constexpr uint16_t effects_loading = 123;
constexpr uint16_t effects_loading = 124; constexpr uint16_t effects_not_connected = 124;
constexpr uint16_t effects_not_connected = 125; constexpr uint16_t climate_target = 125;
constexpr uint16_t climate_target = 126; constexpr uint16_t climate_mode = 126;
constexpr uint16_t climate_mode = 127; constexpr uint16_t climate_fan = 127;
constexpr uint16_t climate_fan = 128; constexpr uint16_t climate_swing = 128;
constexpr uint16_t climate_swing = 129; constexpr uint16_t climate_now = 129;
constexpr uint16_t climate_now = 130; constexpr uint16_t vacuum_check_robot = 130;
constexpr uint16_t vacuum_check_robot = 131; constexpr uint16_t vacuum_auto_suction = 131;
constexpr uint16_t vacuum_auto_suction = 132; constexpr uint16_t vacuum_robot_chooses = 132;
constexpr uint16_t vacuum_robot_chooses = 133; constexpr uint16_t vacuum_per_room = 133;
constexpr uint16_t vacuum_per_room = 134; constexpr uint16_t vacuum_speed_quiet = 134;
constexpr uint16_t vacuum_speed_quiet = 135; constexpr uint16_t vacuum_speed_balanced = 135;
constexpr uint16_t vacuum_speed_balanced = 136; constexpr uint16_t vacuum_speed_turbo = 136;
constexpr uint16_t vacuum_speed_turbo = 137; constexpr uint16_t vacuum_speed_max = 137;
constexpr uint16_t vacuum_speed_max = 138; constexpr uint16_t vacuum_pause_cleaning = 138;
constexpr uint16_t vacuum_pause_cleaning = 139; constexpr uint16_t vacuum_pause = 139;
constexpr uint16_t vacuum_pause = 140; constexpr uint16_t vacuum_resume = 140;
constexpr uint16_t vacuum_resume = 141; constexpr uint16_t vacuum_start_cleaning = 141;
constexpr uint16_t vacuum_start_cleaning = 142; constexpr uint16_t vacuum_clean = 142;
constexpr uint16_t vacuum_clean = 143; constexpr uint16_t vacuum_dock = 143;
constexpr uint16_t vacuum_dock = 144; constexpr uint16_t cover_position = 144;
constexpr uint16_t cover_position = 145; constexpr uint16_t cover_tilt = 145;
constexpr uint16_t cover_tilt = 146; constexpr uint16_t cover_tilt_value = 146;
constexpr uint16_t cover_tilt_value = 147; constexpr uint16_t weather_feels_like = 147;
constexpr uint16_t weather_feels_like = 148; constexpr uint16_t weather_no_forecast = 148;
constexpr uint16_t weather_no_forecast = 149; constexpr uint16_t weather_coming_days = 149;
constexpr uint16_t weather_coming_days = 150; constexpr uint16_t history_no_data = 150;
constexpr uint16_t history_no_data = 151; constexpr uint16_t history_average = 151;
constexpr uint16_t history_average = 152; constexpr uint16_t history_average_of = 152;
constexpr uint16_t history_average_of = 153; constexpr uint16_t history_now = 153;
constexpr uint16_t history_now = 154; constexpr uint16_t history_range_hour = 154;
constexpr uint16_t history_range_hour = 155; constexpr uint16_t history_range_day = 155;
constexpr uint16_t history_range_day = 156; constexpr uint16_t history_range_week = 156;
constexpr uint16_t history_range_week = 157; constexpr uint16_t history_high = 157;
constexpr uint16_t history_high = 158; constexpr uint16_t history_low = 158;
constexpr uint16_t history_low = 159; constexpr uint16_t history_times = 159;
constexpr uint16_t history_times = 160; constexpr uint16_t history_loading = 160;
constexpr uint16_t history_loading = 161; constexpr uint16_t history_empty = 161;
constexpr uint16_t history_empty = 162; constexpr uint16_t history_unavailable = 162;
constexpr uint16_t history_unavailable = 163; constexpr uint16_t media_muted = 163;
constexpr uint16_t media_muted = 164; constexpr uint16_t media_not_playing = 164;
constexpr uint16_t media_not_playing = 165; constexpr uint16_t media_loading = 165;
constexpr uint16_t media_loading = 166; constexpr uint16_t timer_running = 166;
constexpr uint16_t timer_running = 167; constexpr uint16_t timer_paused = 167;
constexpr uint16_t timer_paused = 168; constexpr uint16_t timer_stopped = 168;
constexpr uint16_t timer_stopped = 169; constexpr uint16_t timer_pause = 169;
constexpr uint16_t timer_pause = 170; constexpr uint16_t timer_start = 170;
constexpr uint16_t timer_start = 171; constexpr uint16_t timer_cancel = 171;
constexpr uint16_t timer_cancel = 172; constexpr uint16_t timer_paused_left = 172;
constexpr uint16_t timer_paused_left = 173; constexpr uint16_t sun_name = 173;
constexpr uint16_t sun_name = 174; constexpr uint16_t sun_sunrise = 174;
constexpr uint16_t sun_sunrise = 175; constexpr uint16_t sun_sunset = 175;
constexpr uint16_t sun_sunset = 176; constexpr uint16_t sun_rise = 176;
constexpr uint16_t sun_rise = 177; constexpr uint16_t sun_set = 177;
constexpr uint16_t sun_set = 178; constexpr uint16_t script_running = 178;
constexpr uint16_t script_running = 179; constexpr uint16_t script_never_run = 179;
constexpr uint16_t script_never_run = 180; constexpr uint16_t script_last_time = 180;
constexpr uint16_t script_last_time = 181; constexpr uint16_t script_yesterday_time = 181;
constexpr uint16_t script_yesterday_time = 182; constexpr uint16_t script_last_date = 182;
constexpr uint16_t script_last_date = 183; constexpr uint16_t camera_no_image = 183;
constexpr uint16_t camera_no_image = 184; constexpr uint16_t camera_loading = 184;
constexpr uint16_t camera_loading = 185; constexpr uint16_t camera_live = 185;
constexpr uint16_t camera_live = 186; constexpr uint16_t camera_recording = 186;
constexpr uint16_t camera_recording = 187; constexpr uint16_t camera_tap_to_view = 187;
constexpr uint16_t camera_tap_to_view = 188; constexpr uint16_t camera_no_image_yet = 188;
constexpr uint16_t camera_no_image_yet = 189; constexpr uint16_t calibration_title = 189;
constexpr uint16_t button_activate = 190; constexpr uint16_t calibration_touch_check = 190;
constexpr uint16_t button_run = 191; constexpr uint16_t calibration_hold = 191;
constexpr uint16_t button_press = 192; constexpr uint16_t calibration_crosshair = 192;
constexpr uint16_t calibration_title = 193; constexpr uint16_t calibration_gently = 193;
constexpr uint16_t calibration_touch_check = 194; constexpr uint16_t calibration_same = 194;
constexpr uint16_t calibration_hold = 195; constexpr uint16_t calibration_rejected = 195;
constexpr uint16_t calibration_crosshair = 196; constexpr uint16_t calibration_save_failed = 196;
constexpr uint16_t calibration_gently = 197; constexpr uint16_t ha_on = 197;
constexpr uint16_t calibration_same = 198; constexpr uint16_t ha_off = 198;
constexpr uint16_t calibration_rejected = 199; constexpr uint16_t ha_unavailable = 199;
constexpr uint16_t calibration_save_failed = 200; constexpr uint16_t ha_climate_off = 200;
constexpr uint16_t ha_on = 201; constexpr uint16_t ha_climate_heat = 201;
constexpr uint16_t ha_off = 202; constexpr uint16_t ha_climate_cool = 202;
constexpr uint16_t ha_climate_off = 203; constexpr uint16_t ha_climate_heat_cool = 203;
constexpr uint16_t ha_climate_heat = 204; constexpr uint16_t ha_climate_auto = 204;
constexpr uint16_t ha_climate_cool = 205; constexpr uint16_t ha_climate_dry = 205;
constexpr uint16_t ha_climate_heat_cool = 206; constexpr uint16_t ha_climate_fan_only = 206;
constexpr uint16_t ha_climate_auto = 207; constexpr uint16_t ha_hvac_action_heating = 207;
constexpr uint16_t ha_climate_dry = 208; constexpr uint16_t ha_hvac_action_cooling = 208;
constexpr uint16_t ha_climate_fan_only = 209; constexpr uint16_t ha_hvac_action_idle = 209;
constexpr uint16_t ha_hvac_action_heating = 210; constexpr uint16_t ha_hvac_action_off = 210;
constexpr uint16_t ha_hvac_action_cooling = 211; constexpr uint16_t ha_hvac_action_drying = 211;
constexpr uint16_t ha_hvac_action_idle = 212; constexpr uint16_t ha_hvac_action_fan = 212;
constexpr uint16_t ha_hvac_action_off = 213; constexpr uint16_t ha_hvac_action_preheating = 213;
constexpr uint16_t ha_hvac_action_drying = 214; constexpr uint16_t ha_hvac_action_defrosting = 214;
constexpr uint16_t ha_hvac_action_fan = 215; constexpr uint16_t ha_cover_open = 215;
constexpr uint16_t ha_hvac_action_preheating = 216; constexpr uint16_t ha_cover_closed = 216;
constexpr uint16_t ha_hvac_action_defrosting = 217; constexpr uint16_t ha_cover_opening = 217;
constexpr uint16_t ha_cover_open = 218; constexpr uint16_t ha_cover_closing = 218;
constexpr uint16_t ha_cover_closed = 219; constexpr uint16_t ha_media_playing = 219;
constexpr uint16_t ha_cover_opening = 220; constexpr uint16_t ha_media_paused = 220;
constexpr uint16_t ha_cover_closing = 221; constexpr uint16_t ha_media_idle = 221;
constexpr uint16_t ha_media_playing = 222; constexpr uint16_t ha_media_standby = 222;
constexpr uint16_t ha_media_paused = 223; constexpr uint16_t ha_person_home = 223;
constexpr uint16_t ha_media_idle = 224; constexpr uint16_t ha_person_not_home = 224;
constexpr uint16_t ha_media_standby = 225; constexpr uint16_t ha_sun_above_horizon = 225;
constexpr uint16_t ha_person_home = 226; constexpr uint16_t ha_sun_below_horizon = 226;
constexpr uint16_t ha_person_not_home = 227; constexpr uint16_t ha_vacuum_docked = 227;
constexpr uint16_t ha_sun_above_horizon = 228; constexpr uint16_t ha_vacuum_cleaning = 228;
constexpr uint16_t ha_sun_below_horizon = 229; constexpr uint16_t ha_vacuum_paused = 229;
constexpr uint16_t ha_vacuum_docked = 230; constexpr uint16_t ha_vacuum_returning = 230;
constexpr uint16_t ha_vacuum_cleaning = 231; constexpr uint16_t ha_vacuum_idle = 231;
constexpr uint16_t ha_vacuum_paused = 232; constexpr uint16_t ha_weather_sunny = 232;
constexpr uint16_t ha_vacuum_returning = 233; constexpr uint16_t ha_weather_clear_night = 233;
constexpr uint16_t ha_vacuum_idle = 234; constexpr uint16_t ha_weather_cloudy = 234;
constexpr uint16_t ha_weather_sunny = 235; constexpr uint16_t ha_weather_partlycloudy = 235;
constexpr uint16_t ha_weather_clear_night = 236; constexpr uint16_t ha_weather_rainy = 236;
constexpr uint16_t ha_weather_cloudy = 237; constexpr uint16_t ha_weather_pouring = 237;
constexpr uint16_t ha_weather_partlycloudy = 238; constexpr uint16_t ha_weather_snowy = 238;
constexpr uint16_t ha_weather_rainy = 239; constexpr uint16_t ha_weather_snowy_rainy = 239;
constexpr uint16_t ha_weather_pouring = 240; constexpr uint16_t ha_weather_fog = 240;
constexpr uint16_t ha_weather_snowy = 241; constexpr uint16_t ha_weather_hail = 241;
constexpr uint16_t ha_weather_snowy_rainy = 242; constexpr uint16_t ha_weather_lightning = 242;
constexpr uint16_t ha_weather_fog = 243; constexpr uint16_t ha_weather_lightning_rainy = 243;
constexpr uint16_t ha_weather_hail = 244; constexpr uint16_t ha_weather_windy = 244;
constexpr uint16_t ha_weather_lightning = 245; constexpr uint16_t ha_weather_exceptional = 245;
constexpr uint16_t ha_weather_lightning_rainy = 246; constexpr uint16_t ha_binary_battery_on = 246;
constexpr uint16_t ha_weather_windy = 247; constexpr uint16_t ha_binary_battery_off = 247;
constexpr uint16_t ha_weather_exceptional = 248; constexpr uint16_t ha_binary_battery_charging_on = 248;
constexpr uint16_t ha_binary_battery_on = 249; constexpr uint16_t ha_binary_battery_charging_off = 249;
constexpr uint16_t ha_binary_battery_off = 250; constexpr uint16_t ha_binary_carbon_monoxide_on = 250;
constexpr uint16_t ha_binary_battery_charging_on = 251; constexpr uint16_t ha_binary_carbon_monoxide_off = 251;
constexpr uint16_t ha_binary_battery_charging_off = 252; constexpr uint16_t ha_binary_cold_on = 252;
constexpr uint16_t ha_binary_carbon_monoxide_on = 253; constexpr uint16_t ha_binary_cold_off = 253;
constexpr uint16_t ha_binary_carbon_monoxide_off = 254; constexpr uint16_t ha_binary_connectivity_on = 254;
constexpr uint16_t ha_binary_cold_on = 255; constexpr uint16_t ha_binary_connectivity_off = 255;
constexpr uint16_t ha_binary_cold_off = 256; constexpr uint16_t ha_binary_door_on = 256;
constexpr uint16_t ha_binary_connectivity_on = 257; constexpr uint16_t ha_binary_door_off = 257;
constexpr uint16_t ha_binary_connectivity_off = 258; constexpr uint16_t ha_binary_garage_door_on = 258;
constexpr uint16_t ha_binary_door_on = 259; constexpr uint16_t ha_binary_garage_door_off = 259;
constexpr uint16_t ha_binary_door_off = 260; constexpr uint16_t ha_binary_gas_on = 260;
constexpr uint16_t ha_binary_garage_door_on = 261; constexpr uint16_t ha_binary_gas_off = 261;
constexpr uint16_t ha_binary_garage_door_off = 262; constexpr uint16_t ha_binary_heat_on = 262;
constexpr uint16_t ha_binary_gas_on = 263; constexpr uint16_t ha_binary_heat_off = 263;
constexpr uint16_t ha_binary_gas_off = 264; constexpr uint16_t ha_binary_light_on = 264;
constexpr uint16_t ha_binary_heat_on = 265; constexpr uint16_t ha_binary_light_off = 265;
constexpr uint16_t ha_binary_heat_off = 266; constexpr uint16_t ha_binary_lock_on = 266;
constexpr uint16_t ha_binary_light_on = 267; constexpr uint16_t ha_binary_lock_off = 267;
constexpr uint16_t ha_binary_light_off = 268; constexpr uint16_t ha_binary_moisture_on = 268;
constexpr uint16_t ha_binary_lock_on = 269; constexpr uint16_t ha_binary_moisture_off = 269;
constexpr uint16_t ha_binary_lock_off = 270; constexpr uint16_t ha_binary_motion_on = 270;
constexpr uint16_t ha_binary_moisture_on = 271; constexpr uint16_t ha_binary_motion_off = 271;
constexpr uint16_t ha_binary_moisture_off = 272; constexpr uint16_t ha_binary_moving_on = 272;
constexpr uint16_t ha_binary_motion_on = 273; constexpr uint16_t ha_binary_moving_off = 273;
constexpr uint16_t ha_binary_motion_off = 274; constexpr uint16_t ha_binary_occupancy_on = 274;
constexpr uint16_t ha_binary_moving_on = 275; constexpr uint16_t ha_binary_occupancy_off = 275;
constexpr uint16_t ha_binary_moving_off = 276; constexpr uint16_t ha_binary_opening_on = 276;
constexpr uint16_t ha_binary_occupancy_on = 277; constexpr uint16_t ha_binary_opening_off = 277;
constexpr uint16_t ha_binary_occupancy_off = 278; constexpr uint16_t ha_binary_plug_on = 278;
constexpr uint16_t ha_binary_opening_on = 279; constexpr uint16_t ha_binary_plug_off = 279;
constexpr uint16_t ha_binary_opening_off = 280; constexpr uint16_t ha_binary_power_on = 280;
constexpr uint16_t ha_binary_plug_on = 281; constexpr uint16_t ha_binary_power_off = 281;
constexpr uint16_t ha_binary_plug_off = 282; constexpr uint16_t ha_binary_presence_on = 282;
constexpr uint16_t ha_binary_power_on = 283; constexpr uint16_t ha_binary_presence_off = 283;
constexpr uint16_t ha_binary_power_off = 284; constexpr uint16_t ha_binary_problem_on = 284;
constexpr uint16_t ha_binary_presence_on = 285; constexpr uint16_t ha_binary_problem_off = 285;
constexpr uint16_t ha_binary_presence_off = 286; constexpr uint16_t ha_binary_running_on = 286;
constexpr uint16_t ha_binary_problem_on = 287; constexpr uint16_t ha_binary_running_off = 287;
constexpr uint16_t ha_binary_problem_off = 288; constexpr uint16_t ha_binary_safety_on = 288;
constexpr uint16_t ha_binary_running_on = 289; constexpr uint16_t ha_binary_safety_off = 289;
constexpr uint16_t ha_binary_running_off = 290; constexpr uint16_t ha_binary_smoke_on = 290;
constexpr uint16_t ha_binary_safety_on = 291; constexpr uint16_t ha_binary_smoke_off = 291;
constexpr uint16_t ha_binary_safety_off = 292; constexpr uint16_t ha_binary_sound_on = 292;
constexpr uint16_t ha_binary_smoke_on = 293; constexpr uint16_t ha_binary_sound_off = 293;
constexpr uint16_t ha_binary_smoke_off = 294; constexpr uint16_t ha_binary_tamper_on = 294;
constexpr uint16_t ha_binary_sound_on = 295; constexpr uint16_t ha_binary_tamper_off = 295;
constexpr uint16_t ha_binary_sound_off = 296; constexpr uint16_t ha_binary_update_on = 296;
constexpr uint16_t ha_binary_tamper_on = 297; constexpr uint16_t ha_binary_update_off = 297;
constexpr uint16_t ha_binary_tamper_off = 298; constexpr uint16_t ha_binary_vibration_on = 298;
constexpr uint16_t ha_binary_update_on = 299; constexpr uint16_t ha_binary_vibration_off = 299;
constexpr uint16_t ha_binary_update_off = 300; constexpr uint16_t ha_binary_window_on = 300;
constexpr uint16_t ha_binary_vibration_on = 301; constexpr uint16_t ha_binary_window_off = 301;
constexpr uint16_t ha_binary_vibration_off = 302; constexpr uint16_t ha_button_activate = 302;
constexpr uint16_t ha_binary_window_on = 303; constexpr uint16_t ha_button_run = 303;
constexpr uint16_t ha_binary_window_off = 304; constexpr uint16_t ha_button_press = 304;
constexpr uint16_t date_weekdays = 17; constexpr uint16_t date_weekdays = 17;
constexpr uint16_t date_weekdays_count = 7; constexpr uint16_t date_weekdays_count = 7;
constexpr uint16_t date_weekdays_short = 24; constexpr uint16_t date_weekdays_short = 24;
@@ -278,5 +278,5 @@ constexpr uint16_t date_months_short = 50;
constexpr uint16_t date_months_short_count = 12; constexpr uint16_t date_months_short_count = 12;
} // namespace txt } // namespace txt
constexpr uint16_t KEY_COUNT = 305; constexpr uint16_t KEY_COUNT = 305;
constexpr uint32_t KEYS_HASH = 0x8971C0EBu; constexpr uint32_t KEYS_HASH = 0xC6B1A3EDu;
} // namespace screen_text } // namespace screen_text
+3 -3
View File
@@ -348,9 +348,9 @@ inline std::string neighbour_option(const Tile &t, int direction) {
return options[(current + direction + count) % count]; return options[(current + direction + count) % count];
} }
inline const char *run_label(const std::string &domain) { inline const char *run_label(const std::string &domain) {
if (domain == "scene") return screen_text::tr(screen_text::txt::button_activate); if (domain == "scene") return screen_text::tr(screen_text::txt::ha_button_activate);
if (domain == "script") return screen_text::tr(screen_text::txt::button_run); if (domain == "script") return screen_text::tr(screen_text::txt::ha_button_run);
return screen_text::tr(screen_text::txt::button_press); return screen_text::tr(screen_text::txt::ha_button_press);
} }
// The Home Assistant action behind a key. STEP_* are handled locally (debounced) and return nothing. // The Home Assistant action behind a key. STEP_* are handled locally (debounced) and return nothing.
inline Action key_action(const Tile &t, int command, const std::string &arg = "") { inline Action key_action(const Tile &t, int command, const std::string &arg = "") {
+6
View File
@@ -200,6 +200,12 @@
"vibration_off": "Frei", "vibration_off": "Frei",
"window_on": "Geöffnet", "window_on": "Geöffnet",
"window_off": "Geschlossen" "window_off": "Geschlossen"
},
"unavailable": "Nicht verfügbar",
"button": {
"activate": "Aktivieren",
"run": "Ausführen",
"press": "Drücken"
} }
} }
}, },
+6
View File
@@ -134,6 +134,12 @@
"vibration_off": "Clear", "vibration_off": "Clear",
"window_on": "Open", "window_on": "Open",
"window_off": "Closed" "window_off": "Closed"
},
"unavailable": "Unavailable",
"button": {
"activate": "Activate",
"run": "Run",
"press": "Press"
} }
} }
} }
+6 -6
View File
@@ -143,7 +143,6 @@
"tile": { "tile": {
"tap_to_open": "Tap to open", "tap_to_open": "Tap to open",
"page": "Page {n}", "page": "Page {n}",
"unavailable": "Unavailable",
"refused": "Refused", "refused": "Refused",
"command_sent": "Command sent...", "command_sent": "Command sent...",
"confirmed": "Confirmed by Home Assistant" "confirmed": "Confirmed by Home Assistant"
@@ -248,11 +247,6 @@
"tap_to_view": "Tap to view", "tap_to_view": "Tap to view",
"no_image_yet": "No image yet" "no_image_yet": "No image yet"
}, },
"button": {
"activate": "Activate",
"run": "Run",
"press": "Press"
},
"calibration": { "calibration": {
"title": "Follow the calibration wizard", "title": "Follow the calibration wizard",
"touch_check": "GT911 touch check", "touch_check": "GT911 touch check",
@@ -266,6 +260,7 @@
"ha": { "ha": {
"on": "On", "on": "On",
"off": "Off", "off": "Off",
"unavailable": "Unavailable",
"climate": { "climate": {
"off": "Off", "off": "Off",
"heat": "Heat", "heat": "Heat",
@@ -385,6 +380,11 @@
"vibration_off": "Clear", "vibration_off": "Clear",
"window_on": "Open", "window_on": "Open",
"window_off": "Closed" "window_off": "Closed"
},
"button": {
"activate": "Activate",
"run": "Run",
"press": "Press"
} }
} }
}, },
+6
View File
@@ -200,6 +200,12 @@
"vibration_off": "No detectado", "vibration_off": "No detectado",
"window_on": "Abierto", "window_on": "Abierto",
"window_off": "Cerrado" "window_off": "Cerrado"
},
"unavailable": "No disponible",
"button": {
"activate": "Activar",
"run": "Ejecutar",
"press": "Pulsa"
} }
} }
}, },
+6
View File
@@ -200,6 +200,12 @@
"vibration_off": "Non détectée", "vibration_off": "Non détectée",
"window_on": "Ouvert", "window_on": "Ouvert",
"window_off": "Fermé" "window_off": "Fermé"
},
"unavailable": "Indisponible",
"button": {
"activate": "Activer",
"run": "Exécuter",
"press": "Appuyer"
} }
} }
}, },
+6
View File
@@ -200,6 +200,12 @@
"vibration_off": "Assente", "vibration_off": "Assente",
"window_on": "Aperto", "window_on": "Aperto",
"window_off": "Chiuso" "window_off": "Chiuso"
},
"unavailable": "Non disponibile",
"button": {
"activate": "Attiva",
"run": "Esegui",
"press": "Premi"
} }
} }
}, },
+6 -6
View File
@@ -143,7 +143,6 @@
"tile": { "tile": {
"tap_to_open": "Tik om te openen", "tap_to_open": "Tik om te openen",
"page": "Pagina {n}", "page": "Pagina {n}",
"unavailable": "Niet beschikbaar",
"refused": "Geweigerd", "refused": "Geweigerd",
"command_sent": "Opdracht verstuurd...", "command_sent": "Opdracht verstuurd...",
"confirmed": "Bevestigd door Home Assistant" "confirmed": "Bevestigd door Home Assistant"
@@ -248,11 +247,6 @@
"tap_to_view": "Tik om te bekijken", "tap_to_view": "Tik om te bekijken",
"no_image_yet": "Nog geen beeld" "no_image_yet": "Nog geen beeld"
}, },
"button": {
"activate": "Activeren",
"run": "Uitvoeren",
"press": "Indrukken"
},
"calibration": { "calibration": {
"title": "Volg de kalibratie", "title": "Volg de kalibratie",
"touch_check": "GT911-aanraaktest", "touch_check": "GT911-aanraaktest",
@@ -385,6 +379,12 @@
"vibration_off": "Niet gedetecteerd", "vibration_off": "Niet gedetecteerd",
"window_on": "Open", "window_on": "Open",
"window_off": "Dicht" "window_off": "Dicht"
},
"unavailable": "Niet beschikbaar",
"button": {
"activate": "Inschakelen",
"run": "Uitvoeren",
"press": "Klik"
} }
} }
}, },
+6
View File
@@ -200,6 +200,12 @@
"vibration_off": "brak", "vibration_off": "brak",
"window_on": "otwarto", "window_on": "otwarto",
"window_off": "zamknięto" "window_off": "zamknięto"
},
"unavailable": "niedostępny",
"button": {
"activate": "Aktywuj",
"run": "Uruchom",
"press": "Naciśnij"
} }
} }
}, },
+6
View File
@@ -200,6 +200,12 @@
"vibration_off": "Limpo", "vibration_off": "Limpo",
"window_on": "Aberto", "window_on": "Aberto",
"window_off": "Fechada" "window_off": "Fechada"
},
"unavailable": "Indisponível",
"button": {
"activate": "Ativar",
"run": "Executar",
"press": "Pressione"
} }
} }
}, },
+6 -6
View File
@@ -3,7 +3,7 @@
#pragma once #pragma once
#include "components/smart_display/screen_text.h" #include "components/smart_display/screen_text.h"
static_assert(screen_text::KEYS_HASH == 0x8971C0EBu && screen_text::KEY_COUNT == 305, static_assert(screen_text::KEYS_HASH == 0xC6B1A3EDu && screen_text::KEY_COUNT == 305,
"screen_text_keys.h does not match screen_manager/translations/en.json: run tools/i18n.py header"); "screen_text_keys.h does not match screen_manager/translations/en.json: run tools/i18n.py header");
const char *const screen_text::TABLE[] = { const char *const screen_text::TABLE[] = {
".", ".",
@@ -118,7 +118,6 @@ const char *const screen_text::TABLE[] = {
"{h} h {m}", "{h} h {m}",
"Tap to open", "Tap to open",
"Page {n}", "Page {n}",
"Unavailable",
"Refused", "Refused",
"Command sent...", "Command sent...",
"Confirmed by Home Assistant", "Confirmed by Home Assistant",
@@ -196,9 +195,6 @@ const char *const screen_text::TABLE[] = {
"Recording", "Recording",
"Tap to view", "Tap to view",
"No image yet", "No image yet",
"Activate",
"Run",
"Press",
"Follow the calibration wizard", "Follow the calibration wizard",
"GT911 touch check", "GT911 touch check",
"Hold each tap for a moment", "Hold each tap for a moment",
@@ -209,6 +205,7 @@ const char *const screen_text::TABLE[] = {
"Save failed; try again", "Save failed; try again",
"On", "On",
"Off", "Off",
"Unavailable",
"Off", "Off",
"Heat", "Heat",
"Cool", "Cool",
@@ -310,7 +307,10 @@ const char *const screen_text::TABLE[] = {
"Detected", "Detected",
"Clear", "Clear",
"Open", "Open",
"Closed" "Closed",
"Activate",
"Run",
"Press"
}; };
const char *const screen_text::LANGUAGE = "en"; const char *const screen_text::LANGUAGE = "en";
int screen_text::plural_index(int n) { return n == 1 ? 0 : 1; } int screen_text::plural_index(int n) { return n == 1 ? 0 : 1; }
+24
View File
@@ -200,6 +200,28 @@ def ha_sources():
return sources return sources
# The words Home Assistant's frontend itself shows (its own translation files, not the backend's).
FRONTEND_SOURCES = {'unavailable': 'state.default.unavailable', 'button.activate': 'ui.card.scene.activate',
'button.run': 'ui.card.script.run', 'button.press': 'ui.card.button.press'}
def frontend_words(url, codes):
"""{code: {key: word}} from the translation files Home Assistant's frontend loads (their names carry a hash that the
frontend's app script lists)."""
import urllib.request
page = urllib.request.urlopen(url + '/', timeout=20).read().decode('utf-8', 'replace')
found = {}
for script in dict.fromkeys(re.findall(r'(/frontend_latest/app\.[\w.]+\.js)', page)):
text = urllib.request.urlopen(url + script, timeout=30).read().decode('utf-8', 'replace')
for code in codes:
match = re.search(r'"' + re.escape(code) + r'":\{"nativeName":"[^"]*","hash":"([0-9a-f]+)"', text)
if match and code not in found:
data = json.loads(urllib.request.urlopen(f'{url}/static/translations/{code}-{match.group(1)}.json', timeout=30).read())
flat = dict(generator().flatten(data))
found[code] = {key: flat[source] for key, source in FRONTEND_SOURCES.items() if isinstance(flat.get(source), str)}
return found
def ha_words(write): def ha_words(write):
"""screen.ha of every language from a Home Assistant (HA_URL and HA_TOKEN, or .esphome/ha_url and ha_token): its own """screen.ha of every language from a Home Assistant (HA_URL and HA_TOKEN, or .esphome/ha_url and ha_token): its own
words for states, English included, so a screen says what Home Assistant says (Max, app 0.2.90).""" words for states, English included, so a screen says what Home Assistant says (Max, app 0.2.90)."""
@@ -236,6 +258,7 @@ def ha_words(write):
langs = languages() langs = languages()
found = asyncio.run(fetch(list(langs))) found = asyncio.run(fetch(list(langs)))
frontend = frontend_words(url, list(langs))
status = 0 status = 0
for code, data in langs.items(): for code, data in langs.items():
words = found.get(code) or {} words = found.get(code) or {}
@@ -245,6 +268,7 @@ def ha_words(write):
own[key] = words[source] own[key] = words[source]
else: else:
print(f'{code}: Home Assistant has no {source}') print(f'{code}: Home Assistant has no {source}')
own.update(frontend.get(code, {}))
reference = dict(generator().flatten(data.get('screen', {}).get('ha', {}))) reference = dict(generator().flatten(data.get('screen', {}).get('ha', {})))
if code == 'en': if code == 'en':
for key, word in own.items(): for key, word in own.items():