Release v2.3.0 (#195)

Adds:

- Strategy:
  - SceneCard: Support for Stateful scenes. Closes #190.
  - ValveCard: A card to control an entity of the valve domain. Closes #192.
  - LockView: A view for entities of the lock domain. Closes #171.
- Modules:
  - RegistryFilter: Advanced filtering and sorting of Home Assistant registries.
  - Auxiliaries: Generic utility functions.
  - Debug: Centralized logging and debug level management.
- Tooling:
  - ESLint configuration for linting.
  - Prettier configuration for code formatting.


Removes:

- Redundant types and interface annotations.
- Redundant legacy and unused modules.
- Localization constant from the `Helper` module in favor of a new `localize` utility.

Refactors:

- References regarding change of repository owner.
- Auxiliary functions are moved from the `Helper` module to `Auxiliaries` module.
- `Helper` module is renamed to `Registry` for centralized state and registry management.
- `ControllerCard` is renamed to `HeaderCard`.
- Doc-blocks and comments are updated for clarity and consistency.
- The file tree is restructured for improved clarity, modularity, and scalability.
- Third party type definitions are updated and cleaned up.
- Issue and Pull Request templates.

Optimizations:

- Parallelization of dynamic imports for views and cards.
- Improved error handling and logging throughout the codebase.
- Sanitization of HASS registries after import.
- Enforced stricter and simplified types and interfaces.
- Localization now available at global scope.

Fixes:

- Wrong type in configuration of `HeaderCard`.
- Translation files: Resolved key inconsistencies.
- Removed unnecessary namespaces from types.
- Typos and Grammar.
- Explicit use of `any` types.
- Turn off all entities of a domain with a chip tap action.
- Rendering too small HASS area cards.
- Count of active vacuums.


Bumps:

- Mushroom Strategy
- home-assistant-js-websocket
- superstruct
- ts-loader
- ts-node
- typescript
- webpack
- webpack-cli
This commit is contained in:
Ferry Cools
2025-05-13 07:01:26 +02:00
committed by GitHub
parent 1bfda2ca22
commit a5ec18823d
136 changed files with 7074 additions and 4364 deletions

View File

@ -1,13 +1,8 @@
root = true
[*]
charset = utf-8
end_of_line = lf
ij_continuation_indent_size = 2
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = true
ij_smart_tabs = false
ij_visual_guides =
ij_wrap_on_typing = false
indent_size = 2
indent_style = space
insert_final_newline = true
@ -15,444 +10,5 @@ max_line_length = 120
tab_width = 2
trim_trailing_whitespace = true
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}]
ij_xml_align_attributes = true
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
ij_xml_block_comment_add_space = false
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = true
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = false
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = false
ij_xml_text_wrap = normal
[{*.ats,*.cts,*.mts,*.ts}]
ij_typescript_align_imports = false
ij_typescript_align_multiline_array_initializer_expression = false
ij_typescript_align_multiline_binary_operation = false
ij_typescript_align_multiline_chained_methods = false
ij_typescript_align_multiline_extends_list = false
ij_typescript_align_multiline_for = true
ij_typescript_align_multiline_parameters = true
ij_typescript_align_multiline_parameters_in_calls = false
ij_typescript_align_multiline_ternary_operation = false
ij_typescript_align_object_properties = 0
ij_typescript_align_union_types = false
ij_typescript_align_var_statements = 1
ij_typescript_array_initializer_new_line_after_left_brace = false
ij_typescript_array_initializer_right_brace_on_new_line = false
ij_typescript_array_initializer_wrap = off
ij_typescript_assignment_wrap = off
ij_typescript_binary_operation_sign_on_next_line = false
ij_typescript_binary_operation_wrap = off
ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
ij_typescript_blank_lines_after_imports = 1
ij_typescript_blank_lines_around_class = 0
ij_typescript_blank_lines_around_field = 0
ij_typescript_blank_lines_around_field_in_interface = 0
ij_typescript_blank_lines_around_function = 1
ij_typescript_blank_lines_around_method = 1
ij_typescript_blank_lines_around_method_in_interface = 1
ij_typescript_block_brace_style = end_of_line
ij_typescript_block_comment_add_space = false
ij_typescript_block_comment_at_first_column = true
ij_typescript_call_parameters_new_line_after_left_paren = false
ij_typescript_call_parameters_right_paren_on_new_line = false
ij_typescript_call_parameters_wrap = off
ij_typescript_catch_on_new_line = false
ij_typescript_chained_call_dot_on_new_line = true
ij_typescript_class_brace_style = end_of_line
ij_typescript_comma_on_new_line = false
ij_typescript_do_while_brace_force = never
ij_typescript_else_on_new_line = false
ij_typescript_enforce_trailing_comma = keep
ij_typescript_enum_constants_wrap = on_every_item
ij_typescript_extends_keyword_wrap = off
ij_typescript_extends_list_wrap = off
ij_typescript_field_prefix = _
ij_typescript_file_name_style = relaxed
ij_typescript_finally_on_new_line = false
ij_typescript_for_brace_force = never
ij_typescript_for_statement_new_line_after_left_paren = false
ij_typescript_for_statement_right_paren_on_new_line = false
ij_typescript_for_statement_wrap = off
ij_typescript_force_quote_style = false
ij_typescript_force_semicolon_style = false
ij_typescript_function_expression_brace_style = end_of_line
ij_typescript_if_brace_force = never
ij_typescript_import_merge_members = global
ij_typescript_import_prefer_absolute_path = global
ij_typescript_import_sort_members = true
ij_typescript_import_sort_module_name = false
ij_typescript_import_use_node_resolution = true
ij_typescript_imports_wrap = on_every_item
ij_typescript_indent_case_from_switch = true
ij_typescript_indent_chained_calls = true
ij_typescript_indent_package_children = 0
ij_typescript_jsdoc_include_types = false
ij_typescript_jsx_attribute_value = braces
ij_typescript_keep_blank_lines_in_code = 2
ij_typescript_keep_first_column_comment = true
ij_typescript_keep_indents_on_empty_lines = false
ij_typescript_keep_line_breaks = true
ij_typescript_keep_simple_blocks_in_one_line = true
ij_typescript_keep_simple_methods_in_one_line = false
ij_typescript_line_comment_add_space = true
ij_typescript_line_comment_at_first_column = false
ij_typescript_method_brace_style = end_of_line
ij_typescript_method_call_chain_wrap = off
ij_typescript_method_parameters_new_line_after_left_paren = false
ij_typescript_method_parameters_right_paren_on_new_line = false
ij_typescript_method_parameters_wrap = off
ij_typescript_object_literal_wrap = on_every_item
ij_typescript_object_types_wrap = on_every_item
ij_typescript_parentheses_expression_new_line_after_left_paren = false
ij_typescript_parentheses_expression_right_paren_on_new_line = false
ij_typescript_place_assignment_sign_on_next_line = false
ij_typescript_prefer_as_type_cast = false
ij_typescript_prefer_explicit_types_function_expression_returns = false
ij_typescript_prefer_explicit_types_function_returns = false
ij_typescript_prefer_explicit_types_vars_fields = false
ij_typescript_prefer_parameters_wrap = false
ij_typescript_property_prefix =
ij_typescript_reformat_c_style_comments = true
ij_typescript_space_after_colon = true
ij_typescript_space_after_comma = true
ij_typescript_space_after_dots_in_rest_parameter = false
ij_typescript_space_after_generator_mult = true
ij_typescript_space_after_property_colon = true
ij_typescript_space_after_quest = true
ij_typescript_space_after_type_colon = true
ij_typescript_space_after_unary_not = false
ij_typescript_space_before_async_arrow_lparen = true
ij_typescript_space_before_catch_keyword = true
ij_typescript_space_before_catch_left_brace = true
ij_typescript_space_before_catch_parentheses = true
ij_typescript_space_before_class_lbrace = true
ij_typescript_space_before_class_left_brace = true
ij_typescript_space_before_colon = true
ij_typescript_space_before_comma = false
ij_typescript_space_before_do_left_brace = true
ij_typescript_space_before_else_keyword = true
ij_typescript_space_before_else_left_brace = true
ij_typescript_space_before_finally_keyword = true
ij_typescript_space_before_finally_left_brace = true
ij_typescript_space_before_for_left_brace = true
ij_typescript_space_before_for_parentheses = true
ij_typescript_space_before_for_semicolon = false
ij_typescript_space_before_function_left_parenth = true
ij_typescript_space_before_generator_mult = false
ij_typescript_space_before_if_left_brace = true
ij_typescript_space_before_if_parentheses = true
ij_typescript_space_before_method_call_parentheses = false
ij_typescript_space_before_method_left_brace = true
ij_typescript_space_before_method_parentheses = false
ij_typescript_space_before_property_colon = false
ij_typescript_space_before_quest = true
ij_typescript_space_before_switch_left_brace = true
ij_typescript_space_before_switch_parentheses = true
ij_typescript_space_before_try_left_brace = true
ij_typescript_space_before_type_colon = false
ij_typescript_space_before_unary_not = false
ij_typescript_space_before_while_keyword = true
ij_typescript_space_before_while_left_brace = true
ij_typescript_space_before_while_parentheses = true
ij_typescript_spaces_around_additive_operators = true
ij_typescript_spaces_around_arrow_function_operator = true
ij_typescript_spaces_around_assignment_operators = true
ij_typescript_spaces_around_bitwise_operators = true
ij_typescript_spaces_around_equality_operators = true
ij_typescript_spaces_around_logical_operators = true
ij_typescript_spaces_around_multiplicative_operators = true
ij_typescript_spaces_around_relational_operators = true
ij_typescript_spaces_around_shift_operators = true
ij_typescript_spaces_around_unary_operator = false
ij_typescript_spaces_within_array_initializer_brackets = false
ij_typescript_spaces_within_brackets = false
ij_typescript_spaces_within_catch_parentheses = false
ij_typescript_spaces_within_for_parentheses = false
ij_typescript_spaces_within_if_parentheses = false
ij_typescript_spaces_within_imports = false
ij_typescript_spaces_within_interpolation_expressions = false
ij_typescript_spaces_within_method_call_parentheses = false
ij_typescript_spaces_within_method_parentheses = false
ij_typescript_spaces_within_object_literal_braces = false
ij_typescript_spaces_within_object_type_braces = true
ij_typescript_spaces_within_parentheses = false
ij_typescript_spaces_within_switch_parentheses = false
ij_typescript_spaces_within_type_assertion = false
ij_typescript_spaces_within_union_types = true
ij_typescript_spaces_within_while_parentheses = false
ij_typescript_special_else_if_treatment = true
ij_typescript_ternary_operation_signs_on_next_line = false
ij_typescript_ternary_operation_wrap = off
ij_typescript_union_types_wrap = on_every_item
ij_typescript_use_chained_calls_group_indents = false
ij_typescript_use_double_quotes = true
ij_typescript_use_explicit_js_extension = auto
ij_typescript_use_import_type = auto
ij_typescript_use_path_mapping = always
ij_typescript_use_public_modifier = false
ij_typescript_use_semicolon_after_statement = true
ij_typescript_var_declaration_wrap = normal
ij_typescript_while_brace_force = never
ij_typescript_while_on_new_line = false
ij_typescript_wrap_comments = false
[{*.cjs,*.js}]
ij_javascript_align_imports = false
ij_javascript_align_multiline_array_initializer_expression = false
ij_javascript_align_multiline_binary_operation = false
ij_javascript_align_multiline_chained_methods = false
ij_javascript_align_multiline_extends_list = false
ij_javascript_align_multiline_for = false
ij_javascript_align_multiline_parameters = false
ij_javascript_align_multiline_parameters_in_calls = false
ij_javascript_align_multiline_ternary_operation = false
ij_javascript_align_object_properties = 0
ij_javascript_align_union_types = false
ij_javascript_align_var_statements = 0
ij_javascript_array_initializer_new_line_after_left_brace = false
ij_javascript_array_initializer_right_brace_on_new_line = false
ij_javascript_array_initializer_wrap = off
ij_javascript_assignment_wrap = off
ij_javascript_binary_operation_sign_on_next_line = false
ij_javascript_binary_operation_wrap = off
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
ij_javascript_blank_lines_after_imports = 1
ij_javascript_blank_lines_around_class = 1
ij_javascript_blank_lines_around_field = 0
ij_javascript_blank_lines_around_function = 1
ij_javascript_blank_lines_around_method = 1
ij_javascript_block_brace_style = end_of_line
ij_javascript_block_comment_add_space = false
ij_javascript_block_comment_at_first_column = true
ij_javascript_call_parameters_new_line_after_left_paren = false
ij_javascript_call_parameters_right_paren_on_new_line = false
ij_javascript_call_parameters_wrap = off
ij_javascript_catch_on_new_line = false
ij_javascript_chained_call_dot_on_new_line = true
ij_javascript_class_brace_style = end_of_line
ij_javascript_comma_on_new_line = false
ij_javascript_do_while_brace_force = never
ij_javascript_else_on_new_line = false
ij_javascript_enforce_trailing_comma = keep
ij_javascript_extends_keyword_wrap = off
ij_javascript_extends_list_wrap = off
ij_javascript_field_prefix = _
ij_javascript_file_name_style = relaxed
ij_javascript_finally_on_new_line = false
ij_javascript_for_brace_force = never
ij_javascript_for_statement_new_line_after_left_paren = false
ij_javascript_for_statement_right_paren_on_new_line = false
ij_javascript_for_statement_wrap = off
ij_javascript_force_quote_style = true
ij_javascript_force_semicolon_style = true
ij_javascript_function_expression_brace_style = end_of_line
ij_javascript_if_brace_force = never
ij_javascript_import_merge_members = global
ij_javascript_import_prefer_absolute_path = global
ij_javascript_import_sort_members = true
ij_javascript_import_sort_module_name = false
ij_javascript_import_use_node_resolution = true
ij_javascript_imports_wrap = on_every_item
ij_javascript_indent_case_from_switch = true
ij_javascript_indent_chained_calls = true
ij_javascript_indent_package_children = 0
ij_javascript_jsx_attribute_value = braces
ij_javascript_keep_blank_lines_in_code = 1
ij_javascript_keep_first_column_comment = true
ij_javascript_keep_indents_on_empty_lines = false
ij_javascript_keep_line_breaks = true
ij_javascript_keep_simple_blocks_in_one_line = true
ij_javascript_keep_simple_methods_in_one_line = true
ij_javascript_line_comment_add_space = true
ij_javascript_line_comment_at_first_column = false
ij_javascript_method_brace_style = end_of_line
ij_javascript_method_call_chain_wrap = off
ij_javascript_method_parameters_new_line_after_left_paren = false
ij_javascript_method_parameters_right_paren_on_new_line = false
ij_javascript_method_parameters_wrap = off
ij_javascript_object_literal_wrap = on_every_item
ij_javascript_object_types_wrap = on_every_item
ij_javascript_parentheses_expression_new_line_after_left_paren = false
ij_javascript_parentheses_expression_right_paren_on_new_line = false
ij_javascript_place_assignment_sign_on_next_line = false
ij_javascript_prefer_as_type_cast = false
ij_javascript_prefer_explicit_types_function_expression_returns = false
ij_javascript_prefer_explicit_types_function_returns = false
ij_javascript_prefer_explicit_types_vars_fields = false
ij_javascript_prefer_parameters_wrap = false
ij_javascript_property_prefix =
ij_javascript_reformat_c_style_comments = false
ij_javascript_space_after_colon = true
ij_javascript_space_after_comma = true
ij_javascript_space_after_dots_in_rest_parameter = false
ij_javascript_space_after_generator_mult = true
ij_javascript_space_after_property_colon = true
ij_javascript_space_after_quest = true
ij_javascript_space_after_type_colon = true
ij_javascript_space_after_unary_not = false
ij_javascript_space_before_async_arrow_lparen = true
ij_javascript_space_before_catch_keyword = true
ij_javascript_space_before_catch_left_brace = true
ij_javascript_space_before_catch_parentheses = true
ij_javascript_space_before_class_lbrace = true
ij_javascript_space_before_class_left_brace = true
ij_javascript_space_before_colon = true
ij_javascript_space_before_comma = false
ij_javascript_space_before_do_left_brace = true
ij_javascript_space_before_else_keyword = true
ij_javascript_space_before_else_left_brace = true
ij_javascript_space_before_finally_keyword = true
ij_javascript_space_before_finally_left_brace = true
ij_javascript_space_before_for_left_brace = true
ij_javascript_space_before_for_parentheses = true
ij_javascript_space_before_for_semicolon = false
ij_javascript_space_before_function_left_parenth = true
ij_javascript_space_before_generator_mult = true
ij_javascript_space_before_if_left_brace = true
ij_javascript_space_before_if_parentheses = true
ij_javascript_space_before_method_call_parentheses = false
ij_javascript_space_before_method_left_brace = true
ij_javascript_space_before_method_parentheses = false
ij_javascript_space_before_property_colon = false
ij_javascript_space_before_quest = true
ij_javascript_space_before_switch_left_brace = true
ij_javascript_space_before_switch_parentheses = true
ij_javascript_space_before_try_left_brace = true
ij_javascript_space_before_type_colon = false
ij_javascript_space_before_unary_not = false
ij_javascript_space_before_while_keyword = true
ij_javascript_space_before_while_left_brace = true
ij_javascript_space_before_while_parentheses = true
ij_javascript_spaces_around_additive_operators = true
ij_javascript_spaces_around_arrow_function_operator = true
ij_javascript_spaces_around_assignment_operators = true
ij_javascript_spaces_around_bitwise_operators = true
ij_javascript_spaces_around_equality_operators = true
ij_javascript_spaces_around_logical_operators = true
ij_javascript_spaces_around_multiplicative_operators = true
ij_javascript_spaces_around_relational_operators = true
ij_javascript_spaces_around_shift_operators = true
ij_javascript_spaces_around_unary_operator = false
ij_javascript_spaces_within_array_initializer_brackets = false
ij_javascript_spaces_within_brackets = false
ij_javascript_spaces_within_catch_parentheses = false
ij_javascript_spaces_within_for_parentheses = false
ij_javascript_spaces_within_if_parentheses = false
ij_javascript_spaces_within_imports = false
ij_javascript_spaces_within_interpolation_expressions = false
ij_javascript_spaces_within_method_call_parentheses = false
ij_javascript_spaces_within_method_parentheses = false
ij_javascript_spaces_within_object_literal_braces = true
ij_javascript_spaces_within_object_type_braces = true
ij_javascript_spaces_within_parentheses = false
ij_javascript_spaces_within_switch_parentheses = false
ij_javascript_spaces_within_type_assertion = false
ij_javascript_spaces_within_union_types = true
ij_javascript_spaces_within_while_parentheses = false
ij_javascript_special_else_if_treatment = true
ij_javascript_ternary_operation_signs_on_next_line = true
ij_javascript_ternary_operation_wrap = off
ij_javascript_union_types_wrap = on_every_item
ij_javascript_use_chained_calls_group_indents = false
ij_javascript_use_double_quotes = true
ij_javascript_use_explicit_js_extension = auto
ij_javascript_use_path_mapping = always
ij_javascript_use_public_modifier = false
ij_javascript_use_semicolon_after_statement = true
ij_javascript_var_declaration_wrap = normal
ij_javascript_while_brace_force = never
ij_javascript_while_on_new_line = false
ij_javascript_wrap_comments = false
[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.remarkrc,.stylelintrc,bowerrc,composer.lock,jest.config}]
ij_json_array_wrapping = split_into_lines
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_keep_trailing_comma = false
ij_json_object_wrapping = split_into_lines
ij_json_property_alignment = do_not_align
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = false
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
ij_html_align_attributes = true
ij_html_align_text = false
ij_html_attribute_wrap = normal
ij_html_block_comment_add_space = false
ij_html_block_comment_at_first_column = true
ij_html_do_not_align_children_of_min_lines = 0
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
ij_html_enforce_quotes = false
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
ij_html_keep_blank_lines = 2
ij_html_keep_indents_on_empty_lines = false
ij_html_keep_line_breaks = true
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
ij_html_quote_style = double
ij_html_remove_new_line_before_tags = br
ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
[{*.markdown,*.md}]
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_format_tables = true
ij_markdown_insert_quote_arrows_on_wrap = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_keep_line_breaks_inside_text_blocks = true
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
ij_markdown_wrap_text_if_long = true
ij_markdown_wrap_text_inside_blockquotes = true
[{*.yaml,*.yml}]
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
ij_yaml_indent_sequence_value = true
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_sequence_on_new_line = false
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true
trim_trailing_whitespace = false

View File

@ -0,0 +1,72 @@
{
"root": true,
"env": {
"es2020": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"project": "./tsconfig.json",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"ignorePatterns": [
"dist/",
"node_modules/",
"*.js",
"src/types/homeassistant/",
"src/types/lovelace-mushroom/"
],
"overrides": [
{
"files": [
"webpack.config.ts",
"webpack.dev.config.ts"
],
"parserOptions": {
"project": null
}
}
],
"rules": {
"@typescript-eslint/no-empty-function": "warn",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_"
}
],
"comma-dangle": [
"error",
"always-multiline"
],
"max-len": [
"warn",
{
"code": 120
}
],
"no-console": "off",
"no-empty-function": "off",
"no-unused-vars": "off",
"quotes": [
"error",
"single",
{
"avoidEscape": true
}
],
"semi": [
"error",
"always"
]
}
}

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto eol=lf

View File

@ -1,34 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Versions**
* Mushroom-Strategy:
* HACS:
* Mushroom:
* Home Assistant:
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '…'
2. Click on '…'
3. Scroll down to '…'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

103
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,103 @@
name: Bug Report
description: File a bug report to help us improve the project.
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to report a bug!
Please fill out the requested information to help us understand and reproduce the issue.
- type: input
id: what-happened
attributes:
label: What happened?
description: Briefly describe the bug you encountered.
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
description: Please provide clear and concise steps to reproduce the issue. Be as detailed as possible.
placeholder: |
1. ...
2. ...
validations:
required: true
- type: textarea
id: versions
attributes:
label: Versions
description: Please provide the versions you use of the items below
placeholder: |
- Mushroom Dashboard:
- Lovelace Mushroom:
- Home Assistant:
value: |
- Mushroom Dashboard:
- Lovelace Mushroom:
- Home Assistant:
validations:
required: true
- type: dropdown
id: environment
attributes:
label: Environment
description: What environment were you using when you encountered the bug?
options:
- "Web Browser (specify name and version)"
- "Mobile Application (specify OS and version)"
- "Other (please specify, including version)"
validations:
required: true
- type: input
id: environment-version
attributes:
label: Environment and Version
validations:
required: false
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: What did you expect to happen?
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: Actual Behavior
description: What actually happened?
validations:
required: true
- type: textarea
id: error-logs
attributes:
label: Error Logs (if applicable)
description: If you encountered any error messages or logs, please include them here.
render: plain text
- type: textarea
id: additional-information
attributes:
label: Additional Information
description: Please provide any other relevant information or attachments that might help in understanding the issue.
placeholder: |
Add additional information or drop files here.
- type: checkboxes
id: terms
attributes:
label: Guidelines
description: By submitting this issue, you agree to abide by our [contribution guidelines](blob/main/CONTRIBUTING.md).
options:
- label: I have read and agree to the guidelines.
required: true

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Wiki
url: https://github.com/DigiLive/mushroom-strategy/wiki
about: Please consult to Wiki for more information about the strategy and how to configure it.
- name: Discussions
url: https://github.com/DigiLive/mushroom-strategy/discussions
about: Discuss any other topic about the strategy over here.

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,88 @@
name: Feature Request
description: Suggest a new feature or improvement for the project.
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to suggest a new feature!
Please provide as much detail as possible so we can understand your idea and its potential impact.
- type: input
id: feature-title
attributes:
label: Feature Title
description: Briefly and clearly describe your proposed feature.
placeholder: e.g., Add support for...
validations:
required: true
- type: textarea
id: what-is-it
attributes:
label: What is the feature?
description: Describe the feature in detail. What would it do? How would it work?
validations:
required: true
- type: textarea
id: problem-it-solves
attributes:
label: What problem does this feature solve?
description: Explain the user need or pain point that this feature addresses. Why is it important?
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: Proposed Solution (Optional)
description: |
If you have specific ideas on how this feature could be implemented, please share them here.
Include any technical details or UI/UX suggestions.
- type: dropdown
id: affected-area
attributes:
label: Affected Area (Optional)
description: If this feature primarily relates to a specific part of the project, please indicate it.
options:
- User Interface (UI)
- User Experience (UX)
- Performance
- Security
- Documentation
- Other (please specify below)
default: 0
- type: input
id: affected-area-other
attributes:
label: Other Affected Area (if selected above)
validations:
required: false
- type: textarea
id: alternatives-considered
attributes:
label: Alternatives Considered (Optional)
description: |
Have you considered any alternative solutions or workarounds?
If so, please describe them and why you think this proposed feature is better.
- type: textarea
id: mockups-designs
attributes:
label: Mockups or Designs (Optional)
description: |
If you have any mockups, wireframes, or design ideas to illustrate your feature,
you can describe them here or attach them to the issue.
- type: checkboxes
id: terms
attributes:
label: Guidelines
description: By submitting this issue, you agree to abide by our [contribution guidelines](blob/main/CONTRIBUTING.md).
options:
- label: I have read and agree to the guidelines.
required: true

View File

@ -1,43 +1,13 @@
Please click the `Preview` tab and select the type of Pull Request you are submitting.
- [Bug Fix](?expand=1&template=bugfix.md)
- [Feature](?expand=1&template=feature.md)
> [!NOTE]
> Follow the instructions inside the brackets and remove them.
> In the Title field above, Provide a succinct and descriptive title for the pull request, e.g., "Improve caching mechanism for API calls"
> The above types do not apply to any translation itself.
> To add or fix a translation, select a type below.
>
> Follow the commit guidelines as described at https://github.com/DigiLive/gitChangelog/wiki/1-Introduction
> - [Translation Contribution](?expand=1&template=translation.md)
## Description
[Provide a detailed explanation of the changes you have made. Include the reasons behind these changes and any relevant context. Link any related issues.]
## Type of Change
_Put an `x` in all boxes that apply_
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] Hot fix (corrects a major software bug or fault and should released as quickly as possible.)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] Refactoring
- [ ] Security patch
- [ ] UI/UX improvement
## Testing
[Detail the testing you have performed to ensure that these changes function as intended. Include information about any added tests.]
## Impact
[Discuss the impact of your changes on the project. This might include effects on performance, new dependencies, or changes in behaviour.]
## Additional Information
[Any additional information that reviewers should be aware of.]<br>
[E.g.: Add (before/after) images when visual changes are applied.]
## Checklist
_Put an `x` in all boxes that apply_
- [ ] My code adheres to the [coding and style guidelines](https://github.com/AalianKhan/mushroom-strategy/blob/main/CONTRIBUTING.md) of the project.
- [ ] I have performed a self-review of my own code.
- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have made corresponding changes to the documentation.
- [ ] My changes generate no new warnings.
> [!CAUTION]
> Any Pull Request that does not follow the above types will be rejected.

67
.github/PULL_REQUEST_TEMPLATE/bugfix.md vendored Normal file
View File

@ -0,0 +1,67 @@
# Bug Fix Pull Request
Thank you for contributing to the project by fixing this bug!
Please fill out the following information to help us review your pull request.
---
### Bug Summary
Explain why this fix is needed and what problem it solves.
If it relates to an existing issue, please link it.
See [Linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue).
[Briefly describe the bug you are fixing]
---
### Motivation and Context
Explain why this bug needs to be fixed and the impact it has on the project or users.
[Your explanation here]
---
### List of Changes
[Provide a concise list of the main changes introduced by this pull request to fix the bug.]
- ...
### Steps to Reproduce (if not covered in the linked issue)
If the steps to reproduce the bug are not clearly outlined in the linked issue, please provide them here:
1. ...
2. ...
### Expected Behavior (if not covered in the linked issue)
Describe what the expected behavior should have been before the bug occurred.
[Your description of the expected behavior]
### Actual Behavior (if not covered in the linked issue)
Describe the actual behavior that occurred due to the bug.
[Your description of the actual behavior]
---
### Wiki Updates
[If this bug fix requires any updates to the Wiki, please provide details here.]
---
### Agreements
Please confirm the following by inserting an `x` between the brackets:
- [ ] My code adheres to the [contribution guidelines](blob/main/CONTRIBUTING.md) of the project.
- [ ] My changes generate no new warnings.
- [ ] I have performed a self-review of my own code.
- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have made corresponding changes to the documentation.

View File

@ -0,0 +1,44 @@
# Feature Pull Request
Thank you for contributing to the project!
Please fill out the following information to help us review your pull request.
---
### Feature Summary
[Briefly describe the feature you are proposing]
---
### Motivation and Context
Explain why this feature is needed and what problem it solves.
If it relates to an existing issue, please link it.
See [Linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue).
[Your explanation here]
---
### List of Changes
[Provide a concise list of the main changes introduced by this pull request.]
- ...
### Wiki Updates
[If this bug feature requires any updates to the Wiki, please provide details here.]
---
### Agreements
Please confirm the following by inserting an `x` between the brackets:
- [ ] My code adheres to the [contribution guidelines](blob/main/CONTRIBUTING.md) of the project.
- [ ] My changes generate no new warnings.
- [ ] I have performed a self-review of my own code.
- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have made corresponding changes to the documentation.

View File

@ -0,0 +1,71 @@
# Translation Contribution Pull Request
Thank you for contributing to the project's internationalization efforts!
Please fill out the following information to help us review your translation changes.
---
### Type of Translation Contribution
Please select the type of contribution:
- [ ] Adding a new language translation
- [ ] Fixing errors or improving an existing translation
- [ ] Updating an existing translation with new strings
---
### Target Language
Please specify the language you are adding or modifying:
[List the target language here]
---
### Motivation and Context
Explain why this translation (addition, fix, or update) is needed.
- For fixes, please describe the original error.
- For updates, briefly explain the context of the new strings.
[Your explanation here]
---
### Scope of Changes
Please describe the scope of your translation changes.
Which parts of the project are affected by these translations?
[Describe the affected areas]
---
### List of Changes
Provide a concise list of the main changes you've made in this pull request.
If it's a large update, you can highlight key areas.
- ...
---
### Considerations for Reviewers
Are there any specific areas you would like reviewers to pay extra attention to?
For example, specific terminology, cultural nuances, or consistency with existing translations.
[Any specific instructions for reviewers]
---
### Agreements
Please confirm the following:
- [ ] My code adheres to the [contribution guidelines](blob/main/CONTRIBUTING.md) of the project.
- [ ] I have reviewed the existing translations for consistency.
- [ ] I have used appropriate terminology and followed any project-specific translation guidelines.
- [ ] I have tested the translations to the best of my ability.
- [ ] My changes generate no new warnings or errors related to internationalization.

View File

@ -1,8 +1,8 @@
name: HACS validation
on:
push:
branches:
- main
push:
branches:
- main
jobs:
hacs:
name: HACS Action

View File

@ -18,40 +18,40 @@ jobs:
strategy:
matrix:
node-version: [18.x]
node-version: [ 18.x ]
# Checkout Repository
steps:
- uses: actions/checkout@v3
with:
token: ${{ secrets.WORKFLOW_GIT_ACCESS_TOKEN }}
- uses: actions/checkout@v3
with:
token: ${{ secrets.WORKFLOW_GIT_ACCESS_TOKEN }}
# Build steps
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
# Build steps
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Node Install
run: npm ci
- name: Node Install
run: npm ci
- name: Build Distribution
run: |
npm run build
- name: Build Distribution
run: |
npm run build
- name: Check for file changes
id: checkDiff
run: |
git diff --quiet . || echo "changed=true" >> $GITHUB_OUTPUT
- name: Check for file changes
id: checkDiff
run: |
git diff --quiet . || echo "changed=true" >> $GITHUB_OUTPUT
# Commit and push all changed files.
# Must only affect files that are listed in "paths-ignore".
- name: GIT Commit Distribution Build
# Only run on main branch push (e.g., pull request merge).
if: github.event_name == 'push' && steps.checkDiff.outputs.changed == 'true'
run: |
git config --global user.name "${{ env.CI_COMMIT_AUTHOR }}"
git config --global user.email "ci_activity@noreply.github.com"
git add dist
git commit -m "${{ env.CI_COMMIT_MESSAGE }}"
git push
# Commit and push all changed files.
# Must only affect files that are listed in "paths-ignore".
- name: GIT Commit Distribution Build
# Only run on a main branch push (e.g., pull request merge).
if: github.event_name == 'push' && steps.checkDiff.outputs.changed == 'true'
run: |
git config --global user.name "${{ env.CI_COMMIT_AUTHOR }}"
git config --global user.email "ci_activity@noreply.github.com"
git add dist
git commit -m "${{ env.CI_COMMIT_MESSAGE }}"
git push

1
.gitignore vendored
View File

@ -1 +1,2 @@
/node_modules/
/dist/

14
.prettierrc Normal file
View File

@ -0,0 +1,14 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"endOfLine": "lf",
"jsxSingleQuote": true,
"printWidth": 120,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false
}

View File

@ -38,7 +38,7 @@ decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing
This Code of Conduct applies within all community spaces and also applies when an individual is officially representing
the community in public spaces. Examples of representing our community include using an official e-mail address, posting
via an official social media account, or acting as an appointed representative at an online or offline event.

View File

@ -6,17 +6,18 @@ All types of contributions are encouraged and valued.
See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles
them.
Please make sure to read the relevant section before making your contribution.
It will make it a lot easier for us maintainers and smooth out the experience for all involved.
Please make sure to read the relevant section before making your contribution.
It will make it a lot easier for us maintainers and smooth out the experience for all involved.
The community looks forward to your contributions.
> And if you like the project but just don't have time to contribute, that's fine.
> There are other easy ways to support the project and show your appreciation, which we would also be thrilled about:
> - Star the project
> - Tweet about it
> - Refer this project at your project's readme
> - Mention the project at local meetups and tell your friends/colleagues
> - Sponsor the project at GitHub Sponsors
>
> - Star the project.
> - Tweet about it.
> - Refer to this project in the readme of your project.
> - Mention the project at local meetups and tell your friends/colleagues.
> - Sponsor the project at [GitHub Sponsors][sponsorUrl].
## Table of Contents
@ -43,9 +44,9 @@ By participating, you are expected to uphold this code. Please report unacceptab
> [!TIP]
> Before you ask a question, we assume that you have read the [README](README.md) file or the
> available [Documentation](../../wiki).
> available resources at the [Wiki][wikiUrl] or [Discussions][discussionsUrl].
Before you ask a question, it is best to search for existing [discussions](../../discussions) or [Issues](../../issues)
Before you ask a question, it is best to search for existing [discussions][discussionsUrl] or [Issues][issuesUrl]
that might
help you.
In case you have found a suitable topic and still need clarification, you can address your question in that topic.
@ -53,7 +54,7 @@ It is also advisable to search the internet for answers first.
If you then still feel the need to ask a question and need clarification, we recommend the following:
- Open a new [discussion](../../discussions/new/choose) or [issue](../../issues/new/choose).
- Open a new discussion or issue.
- Provide as much context as you can about what you're running into.
- Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant.
@ -78,11 +79,11 @@ Please complete the following steps in advance to help us fix any potential bug
- Make sure that you are using the latest version.
- Determine if your bug is really a bug and not an error on your side, e.g., using incompatible environment
components/versions (Make sure that you have read the [documentation](../../wiki).
components/versions (Make sure that you have read the [Wiki][wikiUrl].
If you are looking for support, you might want to check [this section](#i-have-a-question)).
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there
is not already a bug report existing for your bug or error in the [bug tracker](../../issues?q=label%3Abug).
- Also make sure to search the internet (including Stack Overflow) to see if users outside the GitHub community have
is not already a report existing for your issue in the [issue tracker][issuesUrl].
- Also, make sure to search the internet (including Stack Overflow) to see if users outside the GitHub community have
discussed the issue.
- Collect information about the bug:
- Stack trace (Traceback).
@ -91,16 +92,16 @@ Please complete the following steps in advance to help us fix any potential bug
- Possibly your input and the output.
- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
#### How Do I Submit a Good Bug Report?
#### How Do I Submit a Good Report?
> [!CAUTION]
> You must never report security related issues, vulnerabilities, or bugs including sensitive information to the issue
> You must never report security-related issues, vulnerabilities, or bugs including sensitive information to the issue
> tracker, or elsewhere in public.
> Instead, sensitive bugs must be sent by email to a project leader.
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
- Open an [issue](../../issues/new/choose).
- Open an [issue][issuesUrl].
- Explain the behavior you would expect and the actual behavior.
- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to
recreate the issue on their own. This usually includes your code.
@ -127,19 +128,19 @@ suggestions.
#### Before Submitting an Enhancement
- Make sure that you are using the latest version.
- Read the [documentation](../../wiki) carefully and find out if the functionality is already covered, maybe by an
individual
configuration.
- Search the [issue tracker](../../issues) to see if the enhancement has already been suggested.
- Read the [Wiki][wikiUrl] carefully and find out if the functionality is already covered, maybe by an
individual configuration.
- Search the [issue tracker][issuesUrl] or [Pull Requests][pullRequestUrl] to see if the enhancement has already been
suggested.
If it has, add a comment to the existing issue instead of opening a new one.
- Find out whether your idea fits with the scope and aims of the project.
It's up to you convince the developers of this feature's merits with a well-reasoned proposal.
It's up to you to convince the developers of this feature's merits with a well-reasoned proposal.
Keep in mind that we want features that will be useful to the majority of our users and not just a small subset.
If you're just targeting a minority of users, consider writing an add-on/plugin library.
#### How Do I Submit a Good Enhancement Suggestion?
Enhancement suggestions are tracked as [GitHub issues](../../issues).
Enhancement suggestions are tracked as [GitHub issues][issuesUrl].
- Use a **clear and descriptive title** for the issue to identify the suggestion.
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
@ -209,3 +210,13 @@ Commit messages must follow [these](https://github.com/DigiLive/gitChangelog/wik
## Attribution
This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)!
[issuesUrl]: https://github.com/DigiLive/mushroom-strategy/issues
[pullRequestUrl]: https://github.com/DigiLive/mushroom-strategy/pulls
[discussionsUrl]: https://github.com/DigiLive/mushroom-strategy/discussions
[wikiUrl]: https://github.com/DigiLive/mushroom-strategy/wiki
[sponsorUrl]: https://github.com/sponsors/DigiLive

View File

@ -63,13 +63,13 @@ Visit the [issues][issuesUrl] page.
[sponsorBadge]: https://img.shields.io/badge/Sponsor_him-%E2%9D%A4-%23db61a2.svg?&logo=github&color=%23fe8e86
[releaseBadge]: https://img.shields.io/badge/Release-v2.3.0-alpha.1-blue
[releaseBadge]: https://img.shields.io/github/v/tag/digilive/mushroom-strategy?filter=v2.3.0&label=Release
<!-- Repository References -->
[repositoryUrl]: https://github.com/DigiLive/mushroom-strategy
[releaseUrl]: https://github.com/DigiLive/mushroom-strategy/releases/tag/v2.3.0-alpha.1
[releaseUrl]: https://github.com/DigiLive/mushroom-strategy/releases/tag/v2.3.0
[issuesUrl]: https://github.com/DigiLive/mushroom-strategy/issues

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,6 @@
{
"name": "Auto generating Mushroom dashboard strategy",
"render_readme": true,
"filename": "mushroom-strategy.js"
"name": "Mushroom Dashboard Strategy ",
"render_readme": true,
"filename": "mushroom-strategy.js",
"homeassistant": "2024.7.0"
}

1580
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +1,49 @@
{
"name": "mushroom-strategy",
"version": "2.2.0",
"description": "Automatically create a dashboard using Mushroom cards",
"version": "2.3.0",
"description": "Automatically generate a dashboard of Mushroom cards.",
"keywords": [
"dashboard",
"strategy",
"mushroom"
],
"homepage": "https://github.com/AalianKhan/mushroom-strategy",
"bugs": "https://github.com/AalianKhan/mushroom-strategy/issues",
"homepage": "https://github.com/DigiLive/mushroom-strategy",
"bugs": "https://github.com/DigiLive/mushroom-strategy/issues",
"license": "MIT",
"author": {
"name": "Aalian Khan"
"name": "Ferry Cools"
},
"contributors": [
{
"name": "Ferry Cools"
"name": "Aalian Khan"
}
],
"funding": {
"type": "github",
"url": "https://github.com/sponsors/DigiLive"
},
"repository": {
"type": "git",
"url": "https://github.com/AalianKhan/mushroom-strategy"
"url": "https://github.com/DigiLive/mushroom-strategy"
},
"dependencies": {
"deepmerge": "^4"
},
"devDependencies": {
"home-assistant-js-websocket": "^9",
"superstruct": "^1",
"ts-loader": "^9.5.0",
"ts-node": "^10",
"typescript": "^5",
"@typescript-eslint/eslint-plugin": "^8.31.0",
"@typescript-eslint/parser": "^8.31.0",
"eslint": "^9.25.1",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^5.2.6",
"home-assistant-js-websocket": "^9.5.0",
"prettier": "^3.5.3",
"superstruct": "^2.0.2",
"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",
"typescript": "^5.8.3",
"version-bump-prompt": "^6",
"webpack": "^5.96.1",
"webpack-cli": "^5.1.4"
"webpack": "^5.99.7",
"webpack-cli": "^6.0.1"
},
"scripts": {
"build-dev": "webpack --config webpack.dev.config.ts",

View File

@ -1,498 +0,0 @@
import {getConfigurationDefaults} from "./configurationDefaults";
import {HassEntities, HassEntity} from "home-assistant-js-websocket";
import deepmerge from "deepmerge";
import {EntityRegistryEntry} from "./types/homeassistant/data/entity_registry";
import {DeviceRegistryEntry} from "./types/homeassistant/data/device_registry";
import {AreaRegistryEntry} from "./types/homeassistant/data/area_registry";
import {generic} from "./types/strategy/generic";
import setupCustomLocalize from "./localize";
import {applyEntityCategoryFilters} from "./utillties/filters";
import StrategyArea = generic.StrategyArea;
/**
* Helper Class
*
* Contains the objects of Home Assistant's registries and helper methods.
*/
class Helper {
/**
* An array of entities from Home Assistant's entity registry.
*
* @type {EntityRegistryEntry[]}
* @private
*/
static #entities: EntityRegistryEntry[];
/**
* An array of entities from Home Assistant's device registry.
*
* @type {DeviceRegistryEntry[]}
* @private
*/
static #devices: DeviceRegistryEntry[];
/**
* An array of entities from Home Assistant's area registry.
*
* @type {StrategyArea[]}
* @private
*/
static #areas: StrategyArea[] = [];
/**
* An array of state entities from Home Assistant's Hass object.
*
* @type {HassEntities}
* @private
*/
static #hassStates: HassEntities;
/**
* Indicates whether this module is initialized.
*
* @type {boolean} True if initialized.
* @private
*/
static #initialized: boolean = false;
/**
* The Custom strategy configuration.
*
* @type {generic.StrategyConfig}
* @private
*/
static #strategyOptions: generic.StrategyConfig;
/**
* Set to true for more verbose information in the console.
*
* @type {boolean}
* @private
*/
static #debug: boolean;
static customLocalize: Function;
/**
* Class constructor.
*
* This class shouldn't be instantiated directly.
* Instead, it should be initialized with method initialize().
*
* @throws {Error} If trying to instantiate this class.
*/
constructor() {
throw new Error("This class should be invoked with method initialize() instead of using the keyword new!");
}
/**
* Custom strategy configuration.
*
* @returns {generic.StrategyConfig}
* @static
*/
static get strategyOptions(): generic.StrategyConfig {
return this.#strategyOptions;
}
/**
* Get the entities from Home Assistant's area registry.
*
* @returns {StrategyArea[]}
* @static
*/
static get areas(): StrategyArea[] {
return this.#areas;
}
/**
* Get the devices from Home Assistant's device registry.
*
* @returns {DeviceRegistryEntry[]}
* @static
*/
static get devices(): DeviceRegistryEntry[] {
return this.#devices;
}
/**
* Get the entities from Home Assistant's entity registry.
*
* @returns {EntityRegistryEntry[]}
* @static
*/
static get entities(): EntityRegistryEntry[] {
return this.#entities;
}
/**
* Get the current debug mode of the mushroom strategy.
*
* @returns {boolean}
* @static
*/
static get debug(): boolean {
return this.#debug;
}
/**
* Initialize this module.
*
* @param {generic.DashBoardInfo} info Strategy information object.
* @returns {Promise<void>}
* @static
*/
static async initialize(info: generic.DashBoardInfo): Promise<void> {
// Initialize properties.
this.customLocalize = setupCustomLocalize(info.hass);
const configurationDefaults = getConfigurationDefaults(this.customLocalize)
this.#strategyOptions = deepmerge(configurationDefaults, info.config?.strategy?.options ?? {});
this.#hassStates = info.hass.states;
this.#debug = this.#strategyOptions.debug;
try {
// Query the registries of Home Assistant.
// noinspection ES6MissingAwait False positive? https://youtrack.jetbrains.com/issue/WEB-63746
[Helper.#entities, Helper.#devices, Helper.#areas] = await Promise.all([
info.hass.callWS({type: "config/entity_registry/list"}) as Promise<EntityRegistryEntry[]>,
info.hass.callWS({type: "config/device_registry/list"}) as Promise<DeviceRegistryEntry[]>,
info.hass.callWS({type: "config/area_registry/list"}) as Promise<AreaRegistryEntry[]>,
]);
} catch (e) {
Helper.logError("An error occurred while querying Home assistant's registries!", e);
throw 'Check the console for details';
}
// Create and add the undisclosed area if not hidden in the strategy options.
if (!this.#strategyOptions.areas.undisclosed?.hidden) {
this.#strategyOptions.areas.undisclosed = {
...configurationDefaults.areas.undisclosed,
...this.#strategyOptions.areas.undisclosed,
};
// Make sure the custom configuration of the undisclosed area doesn't overwrite the area_id.
this.#strategyOptions.areas.undisclosed.area_id = "undisclosed";
this.#areas.push(this.#strategyOptions.areas.undisclosed);
}
// Merge custom areas of the strategy options into strategy areas.
this.#areas = Helper.areas.map(area => {
return {...area, ...this.#strategyOptions.areas?.[area.area_id]};
});
// Sort strategy areas by order first and then by name.
this.#areas.sort((a, b) => {
return (a.order ?? Infinity) - (b.order ?? Infinity) || a.name.localeCompare(b.name);
});
// Sort custom and default views of the strategy options by order first and then by title.
this.#strategyOptions.views = Object.fromEntries(
Object.entries(this.#strategyOptions.views).sort(([, a], [, b]) => {
return (a.order ?? Infinity) - (b.order ?? Infinity) || (a.title ?? "undefined").localeCompare(b.title ?? "undefined");
}),
);
// Sort custom and default domains of the strategy options by order first and then by title.
this.#strategyOptions.domains = Object.fromEntries(
Object.entries(this.#strategyOptions.domains).sort(([, a], [, b]) => {
return (a.order ?? Infinity) - (b.order ?? Infinity) || (a.title ?? "undefined").localeCompare(b.title ?? "undefined");
}),
);
this.#initialized = true;
}
/**
* Get the initialization status of the Helper class.
*
* @returns {boolean} True if this module is initialized.
* @static
*/
static isInitialized(): boolean {
return this.#initialized;
}
/**
* Get a template string to define the number of a given domain's entities with a certain state.
*
* States are compared against a given value by a given operator.
* States `unavailable` and `unknown` are always excluded.
*
* @param {string} domain The domain of the entities.
* @param {string} operator The comparison operator between state and value.
* @param {string} value The value to which the state is compared against.
*
* @return {string} The template string.
* @static
*/
static getCountTemplate(domain: string, operator: string, value: string): string {
/**
* Array of entity state-entries, filtered by domain.
*
* Each element contains a template-string which is used to access home assistant's state machine (state object) in
* a template.
* E.g. "states['light.kitchen']"
*
* The array excludes hidden and disabled entities.
*
* @type {string[]}
*/
const states: string[] = [];
if (!this.isInitialized()) {
console.warn("Helper class should be initialized before calling this method!");
}
// Get the state of entities which are linked to the given area.
for (const area of this.#areas) {
let entities = this.getDeviceEntities(area, domain);
// Exclude hidden Config and Diagnostic entities.
entities = applyEntityCategoryFilters(entities, domain);
const newStates = entities.map((entity) => `states['${entity.entity_id}']`);
states.push(...newStates);
}
return (
`{% set entities = [${states}] %}
{{ entities
| selectattr('state','${operator}','${value}')
| selectattr('state','ne','unavailable')
| selectattr('state','ne','unknown')
| list
| count
}}`
);
}
/**
* Get device entities from the entity registry, filtered by area and domain.
*
* The entity registry is a registry where Home-Assistant keeps track of all entities.
* A device is represented in Home Assistant via one or more entities.
*
* The result excludes hidden and disabled entities.
*
* @param {AreaRegistryEntry} area Area entity.
* @param {string} [domain] The domain of the entity-id.
*
* @return {EntityRegistryEntry[]} Array of device entities.
* @static
*/
static getDeviceEntities(area: AreaRegistryEntry, domain?: string): EntityRegistryEntry[] {
if (!this.isInitialized()) {
console.warn("Helper class should be initialized before calling this method!");
}
// Get the ID of the devices which are linked to the given area.
const areaDeviceIds = this.#devices.filter((device) => {
return (device.area_id ?? "undisclosed") === area.area_id;
}).map((device: DeviceRegistryEntry) => {
return device.id;
});
// Return the entities of which all conditions of the callback function are met. @see areaFilterCallback.
return this.#entities.filter(
this.#areaFilterCallback, {
area: area,
domain: domain,
areaDeviceIds: areaDeviceIds,
})
.sort((a, b) => {
return (a.original_name ?? "undefined").localeCompare(b.original_name ?? "undefined");
});
}
/**
* Get state entities, filtered by area and domain.
*
* The result excludes hidden and disabled entities.
*
* @param {AreaRegistryEntry} area Area entity.
* @param {string} domain Domain of the entity-id.
*
* @return {HassEntity[]} Array of state entities.
*/
static getStateEntities(area: AreaRegistryEntry, domain: string): HassEntity[] {
if (!this.isInitialized()) {
console.warn("Helper class should be initialized before calling this method!");
}
const states: HassEntity[] = [];
// Create a map for the hassEntities and devices {id: object} to improve lookup speed.
const entityMap: {
[s: string]: EntityRegistryEntry;
} = Object.fromEntries(this.#entities.map((entity) => [entity.entity_id, entity]));
const deviceMap: {
[s: string]: DeviceRegistryEntry;
} = Object.fromEntries(this.#devices.map((device) => [device.id, device]));
// Get states whose entity-id starts with the given string.
const stateEntities = Object.values(this.#hassStates).filter(
(state) => state.entity_id.startsWith(`${domain}.`),
);
for (const state of stateEntities) {
const hassEntity = entityMap[state.entity_id];
const device = deviceMap[hassEntity?.device_id ?? ""];
// Collect states of which any (whichever comes first) of the conditions below are met:
// 1. The linked entity is linked to the given area.
// 2. The entity is linked to a device, and the linked device is linked to the given area.
if (
(hassEntity?.area_id === area.area_id)
|| (device && device.area_id === area.area_id)
) {
states.push(state);
}
}
return states;
}
/**
* Get the state object of a HASS entity.
*
* @param {EntityRegistryEntry} entity The entity for which to get the state.
* @returns {HassEntity | undefined} The state object of the entity, or undefined if not found.
* @static
*/
static getEntityState(entity: EntityRegistryEntry): HassEntity | undefined {
return this.#hassStates[entity.entity_id];
}
/**
* Sanitize a classname.
*
* The name is sanitized by capitalizing the first character of the name or after an underscore.
* Underscores are removed.
*
* @param {string} className Name of the class to sanitize.
* @returns {string} The sanitized classname.
*/
static sanitizeClassName(className: string): string {
className = className.charAt(0).toUpperCase() + className.slice(1);
return className.replace(/([-_][a-z])/g, (group) => group
.toUpperCase()
.replace("-", "")
.replace("_", ""),
);
}
/**
* Get the ids of the views which aren't set to hidden in the strategy options.
*
* @return {string[]} An array of view ids.
*/
static getExposedViewIds(): string[] {
if (!this.isInitialized()) {
console.warn("Helper class should be initialized before calling this method!");
}
return this.#getObjectKeysByPropertyValue(this.#strategyOptions.views, "hidden", false);
}
/**
* Get the ids of the domain ids which aren't set to hidden in the strategy options.
*
* @return {string[]} An array of domain ids.
*/
static getExposedDomainIds(): string[] {
if (!this.isInitialized()) {
console.warn("Helper class should be initialized before calling this method!");
}
return this.#getObjectKeysByPropertyValue(this.#strategyOptions.domains, "hidden", false);
}
/**
* Callback function for filtering entities.
*
* Entities of which all the conditions below are met are kept:
* 1. The entity is not hidden and the entity's device is not hidden by the strategy options.
* 2. The entity is not hidden and is not disabled by Hass.
* 3. The entity's domain matches the given domain.
* 4. The entity itself or else the entity's device is linked to the given area.
*
* @param {EntityRegistryEntry} entity The current Hass entity to evaluate.
* @this {AreaFilterContext}
*
* @return {boolean} True to keep the entity.
* @static
*/
static #areaFilterCallback(
this: {
area: AreaRegistryEntry,
areaDeviceIds: string[],
domain: string,
},
entity: EntityRegistryEntry): boolean {
const cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
const deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"];
const entityUnhidden =
!cardOptions?.hidden && !deviceOptions?.hidden // Condition 1.
&& entity.hidden_by === null && entity.disabled_by === null; // Condition 2.
const domainMatches = this.domain === undefined || entity.entity_id.startsWith(`${this.domain}.`); // Condition 3.
// Condition 4.
const entityLinked = this.area.area_id === "undisclosed"
// Undisclosed area.
? !entity.area_id && (this.areaDeviceIds.includes(entity.device_id ?? "") || !entity.device_id)
// Area is a hass entity. Note: entity.area_id is set to null when using device's area.
: entity.area_id === this.area.area_id || (!entity.area_id && this.areaDeviceIds.includes(entity.device_id ?? ""));
return (entityUnhidden && domainMatches && entityLinked);
}
/**
* Get the keys of nested objects by its property value.
*
* @param {Object<string, any>} object An object of objects.
* @param {string|number} property The name of the property to evaluate.
* @param {*} value The value which the property should match.
*
* @return {string[]} An array with keys.
*/
static #getObjectKeysByPropertyValue(
object: { [k: string]: any },
property: string, value: any
): string[] {
const keys: string[] = [];
for (const key of Object.keys(object)) {
if (object[key][property] === value) {
keys.push(key);
}
}
return keys;
}
/**
* Logs an error message to the console.
*
* @param {string} userMessage - The error message to display.
* @param {unknown} [e] - (Optional) The error object or additional information.
*
* @return {void}
*/
static logError(userMessage: string, e?: unknown): void {
if (Helper.debug) {
console.error(userMessage, e);
return;
}
console.error(userMessage);
}
}
export {Helper};

298
src/Registry.ts Normal file
View File

@ -0,0 +1,298 @@
import deepmerge from 'deepmerge';
import { HassEntities } from 'home-assistant-js-websocket';
import { AreaRegistryEntry } from './types/homeassistant/data/area_registry';
import { DeviceRegistryEntry } from './types/homeassistant/data/device_registry';
import { EntityRegistryEntry } from './types/homeassistant/data/entity_registry';
import {
AllDomainsConfig,
DashboardInfo,
isSortable,
SingleDomainConfig,
StrategyArea,
StrategyConfig,
StrategyViewConfig,
SupportedDomains,
SupportedViews,
} from './types/strategy/strategy-generics';
import { logMessage, lvlFatal, lvlOff, lvlWarn, setDebugLevel } from './utilities/debug';
import setupCustomLocalize from './utilities/localize';
import RegistryFilter from './utilities/RegistryFilter';
/**
* Registry Class
*
* Contains the entries of Home Assistant's registries and Strategy configuration.
*/
class Registry {
/** Entries of Home Assistant's entity registry. */
private static _entities: EntityRegistryEntry[];
/** Entries of Home Assistant's device registry. */
private static _devices: DeviceRegistryEntry[];
/** Entries of Home Assistant's area registry. */
private static _areas: StrategyArea[] = [];
/** Entries of Home Assistant's state registry */
private static _hassStates: HassEntities;
/** Indicates whether this module is initialized. */
private static _initialized: boolean = false;
/** The Custom strategy configuration. */
private static _strategyOptions: StrategyConfig;
/**
* Class constructor.
*
* @remarks
* This class shouldn't be instantiated directly.
* Instead, method {@link Registry.initialize} must be invoked.
*/
// noinspection JSUnusedLocalSymbols
// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {}
/** The configuration of the strategy. */
static get strategyOptions(): StrategyConfig {
return Registry._strategyOptions;
}
/**
* Home Assistant's Area registry.
*
* @remarks
* This module makes changes to the registry at {@link Registry.initialize}.
*/
static get areas(): StrategyArea[] {
return Registry._areas;
}
/**
* Home Assistant's Device registry.
*
* @remarks
* This module makes changes to the registry at {@link Registry.initialize}.
*/
static get devices(): DeviceRegistryEntry[] {
return Registry._devices;
}
/**
* Home Assistant's Entity registry.
*
* @remarks
* This module makes changes to the registry at {@link Registry.initialize}.
*/
static get entities(): EntityRegistryEntry[] {
return Registry._entities;
}
/** Home Assistant's State registry. */
static get hassStates(): HassEntities {
return Registry._hassStates;
}
/** Get the initialization status of the Registry class. */
static get initialized(): boolean {
return Registry._initialized;
}
/**
* Initialize this module.
*
* Imports the registries of Home Assistant and the strategy options.
*
* After importing, the registries are sanitized according to the provided strategy options.
* This method must be called before using any other Registry functionality that depends on the imported data.
*
* @param {DashboardInfo} info Strategy information object.
*/
static async initialize(info: DashboardInfo): Promise<void> {
setupCustomLocalize(info.hass);
// Import the Hass States and strategy options.
Registry._hassStates = info.hass.states;
const { ConfigurationDefaults } = await import('./configurationDefaults');
try {
Registry._strategyOptions = deepmerge(ConfigurationDefaults, info.config?.strategy?.options ?? {});
} catch (e) {
logMessage(lvlFatal, 'Error importing strategy options!', e);
}
setDebugLevel(Registry.strategyOptions.debug ? lvlFatal : lvlOff);
// Import the registries of Home Assistant.
try {
// noinspection ES6MissingAwait False positive? https://youtrack.jetbrains.com/issue/WEB-63746
[Registry._entities, Registry._devices, Registry._areas] = await Promise.all([
info.hass.callWS({ type: 'config/entity_registry/list' }) as Promise<EntityRegistryEntry[]>,
info.hass.callWS({ type: 'config/device_registry/list' }) as Promise<DeviceRegistryEntry[]>,
info.hass.callWS({ type: 'config/area_registry/list' }) as Promise<AreaRegistryEntry[]>,
]);
} catch (e) {
logMessage(lvlFatal, 'Error importing Home Assistant registries!', e);
}
// Process the entries of the Strategy Options.
Registry._strategyOptions.extra_views.map((view) => ({
...view,
subview: false,
}));
// Process entries of the HASS entity registry.
Registry._entities = new RegistryFilter(Registry.entities)
.not()
.whereEntityCategory('config')
.not()
.whereEntityCategory('diagnostic')
.isNotHidden()
.whereDisabledBy(null)
.orderBy(['name', 'original_name'], 'asc')
.toList();
Registry._entities = Registry.entities.map((entity) => ({
...entity,
area_id: entity.area_id ?? 'undisclosed',
}));
// Process entries of the HASS device registry.
Registry._devices = new RegistryFilter(Registry.devices)
.isNotHidden()
.whereDisabledBy(null)
.orderBy(['name_by_user', 'name'], 'asc')
.toList();
Registry._devices = Registry.devices.map((device) => ({
...device,
area_id: device.area_id ?? 'undisclosed',
}));
// Process entries of the HASS area registry.
if (Registry.strategyOptions.areas._?.hidden) {
Registry._areas = [];
} else {
// Create and add the undisclosed area if not hidden in the strategy options.
if (!Registry.strategyOptions.areas.undisclosed?.hidden) {
Registry.areas.push(ConfigurationDefaults.areas.undisclosed);
}
// Merge area configurations of the Strategy options into the entries of the area registry.
// TODO: Check for to do the same for devices.
Registry._areas = Registry.areas.map((area) => {
return { ...area, ...Registry.strategyOptions.areas['_'], ...Registry.strategyOptions.areas?.[area.area_id] };
});
// Ensure the custom configuration of the undisclosed area doesn't overwrite the area_id.
Registry.strategyOptions.areas.undisclosed.area_id = 'undisclosed';
// Remove hidden areas if configured as so and sort them by name.
Registry._areas = new RegistryFilter(Registry.areas).isNotHidden().orderBy(['order', 'name'], 'asc').toList();
}
// Sort views by order first and then by title.
const sortViews = () => {
const entries = Object.entries(Registry.strategyOptions.views);
Registry.strategyOptions.views = Object.fromEntries(
entries.sort(([_, a], [__, b]) => {
return (a.order ?? Infinity) - (b.order ?? Infinity) || (a.title ?? '').localeCompare(b.title ?? '');
}),
) as Record<SupportedViews, StrategyViewConfig>;
};
sortViews();
// Sort domains by order first and then by title.
const sortDomains = () => {
const entries = Object.entries(Registry.strategyOptions.domains);
Registry.strategyOptions.domains = Object.fromEntries(
entries.sort(([, a], [, b]) => {
if (isSortable(a) && isSortable(b)) {
return (a.order ?? Infinity) - (b.order ?? Infinity) || (a.title ?? '').localeCompare(b.title ?? '');
}
return 0; // Maintain the original order when none or only one item is sortable.
}),
) as { [K in SupportedDomains]: K extends '_' ? AllDomainsConfig : SingleDomainConfig };
};
sortDomains();
// Sort extra views by order first and then by title.
// TODO: Add sorting to the wiki.
const sortExtraViews = () => {
Registry.strategyOptions.extra_views.sort((a, b) => {
return (a.order ?? Infinity) - (b.order ?? Infinity) || (a.title ?? '').localeCompare(b.title ?? '');
});
};
sortExtraViews();
Registry._initialized = true;
}
/**
* Get a template string to define the number of a given domain's entities with a certain state.
*
* States are compared against a given value by a given operator.
* States `unavailable` and `unknown` are always excluded.
*
* @param {string} domain The domain of the entities.
* @param {string} operator The comparison operator between state and value.
* @param {string} value The value to which the state is compared against.
*/
static getCountTemplate(domain: SupportedDomains, operator: string, value: string): string {
// noinspection JSMismatchedCollectionQueryUpdate
/**
* Array of entity state-entries, filtered by domain.
*
* Each element contains a template-string which is used to access home assistant's state machine (state object) in
* a template; E.g. `states['light.kitchen']`.
*/
const states: string[] = [];
if (!Registry.initialized) {
logMessage(lvlWarn, 'Registry not initialized!');
return '?';
}
states.push(
...new RegistryFilter(Registry.entities)
.whereDomain(domain)
.where((entity) => !entity.entity_id.endsWith('_stateful_scene'))
.toList()
.map((entity) => `states['${entity.entity_id}']`),
);
return `{% set entities = [${states}] %}
{{ entities
| selectattr('state','${operator}','${value}')
| selectattr('state','ne','unavailable')
| selectattr('state','ne','unknown')
| list
| count
}}`;
}
/**
* Get the names of the specified type which aren't set to hidden in the strategy options.
*
* @param {string} type The type of options to filter ("domain", "view", "chip").
*
* @returns {string[]} For domains and views: names of items that aren't hidden.
* For chips: names of items that are explicitly set to true.
*/
static getExposedNames(type: 'domain' | 'view' | 'chip'): string[] {
// TODO: Align chip with other types.
if (type === 'chip') {
return Object.entries(Registry.strategyOptions.chips)
.filter(([_, value]) => value === true)
.map(([key]) => key.split('_')[0]);
}
const group = Registry.strategyOptions[`${type}s`] as Record<string, { hidden?: boolean }>;
return Object.keys(group).filter((key) => key !== '_' && key !== 'default' && !group[key].hidden);
}
}
export { Registry };

View File

@ -1,59 +1,59 @@
import {Helper} from "../Helper";
import {cards} from "../types/strategy/cards";
import {generic} from "../types/strategy/generic";
import {EntityCardConfig} from "../types/lovelace-mushroom/cards/entity-card-config";
import { Registry } from '../Registry';
import { LovelaceCardConfig } from '../types/homeassistant/data/lovelace/config/card';
import { AbstractCardConfig } from '../types/strategy/strategy-cards';
import { RegistryEntry } from '../types/strategy/strategy-generics';
import { logMessage, lvlFatal } from '../utilities/debug';
/**
* Abstract Card Class
*
* To create a new card, extend the new class with this one.
* To create a card configuration, this class should be extended by a child class.
* Child classes should override the default configuration so the card correctly reflects the entity.
*
* @class
* @abstract
* @remarks
* Before using this class, the Registry module must be initialized by calling {@link Registry.initialize}.
*/
abstract class AbstractCard {
/**
* Entity to create the card for.
*
* @type {generic.RegistryEntry}
*/
entity: generic.RegistryEntry;
/** The registry entry this card represents. */
readonly entity: RegistryEntry;
/**
* Configuration of the card.
* The card configuration for this entity.
*
* @type {EntityCardConfig}
* Child classes should override this property to reflect their own card type and options.
*/
config: EntityCardConfig = {
type: "custom:mushroom-entity-card",
icon: "mdi:help-circle",
protected configuration: LovelaceCardConfig = {
type: 'custom:mushroom-entity-card',
icon: 'mdi:help-circle',
};
/**
* Class constructor.
*
* @param {generic.RegistryEntry} entity The hass entity to create a card for.
* @throws {Error} If the Helper module isn't initialized.
* @param {RegistryEntry} entity The registry entry to create a card configuration for.
*
* @remarks
* Before this class can be used, the Registry module must be initialized by calling {@link Registry.initialize}.
*/
protected constructor(entity: generic.RegistryEntry) {
if (!Helper.isInitialized()) {
throw new Error("The Helper module must be initialized before using this one.");
protected constructor(entity: RegistryEntry) {
if (!Registry.initialized) {
logMessage(lvlFatal, 'Registry not initialized!');
}
this.entity = entity;
}
/**
* Get a card.
* Get a card configuration.
*
* @return {cards.AbstractCardConfig} A card object.
* The configuration should be set by any of the child classes so the card correctly reflects an entity.
*/
getCard(): cards.AbstractCardConfig {
getCard(): AbstractCardConfig {
return {
...this.config,
entity: "entity_id" in this.entity ? this.entity.entity_id : undefined,
...this.configuration,
entity: 'entity_id' in this.entity ? this.entity.entity_id : undefined,
};
}
}
export {AbstractCard};
export default AbstractCard;

View File

@ -1,68 +1,52 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {AreaRegistryEntry} from "../types/homeassistant/data/area_registry";
import {TemplateCardConfig} from "../types/lovelace-mushroom/cards/template-card-config";
import { AreaRegistryEntry } from '../types/homeassistant/data/area_registry';
import { TemplateCardConfig } from '../types/lovelace-mushroom/cards/template-card-config';
import AbstractCard from './AbstractCard';
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
/**
* Area Card Class
*
* Used to create a card for an entity of the area domain.
*
* @class
* @extends AbstractCard
* Used to create card configuration for an entry of the HASS area registry.
*/
class AreaCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {TemplateCardConfig}
* @private
*/
#defaultConfig: TemplateCardConfig = {
type: "custom:mushroom-template-card",
primary: undefined,
icon: "mdi:floor-plan",
icon_color: "blue",
tap_action: {
action: "navigate",
navigation_path: "",
},
hold_action: {
action: "none",
},
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): TemplateCardConfig {
return {
type: 'custom:mushroom-template-card',
primary: undefined,
icon: 'mdi:floor-plan',
icon_color: 'blue',
tap_action: { action: 'navigate', navigation_path: '' },
hold_action: { action: 'none' },
};
}
/**
* Class constructor.
*
* @param {AreaRegistryEntry} area The area entity to create a card for.
* @param {cards.TemplateCardOptions} [options={}] Options for the card.
*
* @throws {Error} If the Helper module isn't initialized.
* @param {AreaRegistryEntry} area The HASS area to create a card configuration for.
* @param {TemplateCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(area: AreaRegistryEntry, options: cards.TemplateCardOptions = {}) {
constructor(area: AreaRegistryEntry, customConfiguration?: TemplateCardConfig) {
super(area);
const configuration = AreaCard.getDefaultConfig();
let customConfig = customConfiguration;
configuration.primary = area.name;
configuration.icon = area.icon || configuration.icon;
if (configuration.tap_action && 'navigation_path' in configuration.tap_action) {
configuration.tap_action.navigation_path = area.area_id;
}
// Don't override the default card type if default is set in the strategy options.
if (options.type === "default") {
delete options.type;
if (customConfig && customConfig.type === 'default') {
customConfig = { ...customConfig, type: configuration.type };
}
// Initialize the default configuration.
this.#defaultConfig.primary = area.name;
if (this.#defaultConfig.tap_action && ("navigation_path" in this.#defaultConfig.tap_action)) {
this.#defaultConfig.tap_action.navigation_path = area.area_id;
}
// Overwrite the default icon to the user-defined icon in hass.
if (area.icon) {
this.#defaultConfig.icon = area.icon;
}
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...configuration, ...customConfig };
}
}
export {AreaCard};
export default AreaCard;

View File

@ -1,42 +1,35 @@
import {SensorCard} from "./SensorCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {EntityCardConfig} from "../types/lovelace-mushroom/cards/entity-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { EntityCardConfig } from '../types/lovelace-mushroom/cards/entity-card-config';
import SensorCard from './SensorCard';
/**
* Sensor Card Class
*
* Used to create a card for controlling an entity of the binary_sensor domain.
*
* @class
* @extends SensorCard
* Used to create a card configuration to control an entity of the binary_sensor domain.
*/
class BinarySensorCard extends SensorCard {
/**
* Default configuration of the card.
*
* @type {EntityCardConfig}
* @private
*/
#defaultConfig: EntityCardConfig = {
type: "custom:mushroom-entity-card",
icon: "mdi:power-cycle",
icon_color: "green",
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): EntityCardConfig {
return {
type: 'custom:mushroom-entity-card',
icon: 'mdi:power-cycle',
icon_color: 'green',
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.EntityCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {EntityCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.EntityCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: EntityCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...BinarySensorCard.getDefaultConfig(), ...customConfiguration };
}
}
export {BinarySensorCard};
export default BinarySensorCard;

View File

@ -1,44 +1,37 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {PictureEntityCardConfig} from "../types/homeassistant/panels/lovelave/cards/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { PictureEntityCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
import AbstractCard from './AbstractCard';
/**
* Camera Card Class
*
* Used to create a card for controlling an entity of the camera domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the camera domain.
*/
class CameraCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {PictureEntityCardConfig}
* @private
*/
#defaultConfig: PictureEntityCardConfig = {
entity: "",
type: "picture-entity",
show_name: false,
show_state: false,
camera_view: "live",
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): PictureEntityCardConfig {
return {
entity: '',
type: 'picture-entity',
show_name: false,
show_state: false,
camera_view: 'live',
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.PictureEntityCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {PictureEntityCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.PictureEntityCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: PictureEntityCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...CameraCard.getDefaultConfig(), ...customConfiguration };
}
}
export {CameraCard};
export default CameraCard;

View File

@ -1,48 +1,36 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {ClimateCardConfig} from "../types/lovelace-mushroom/cards/climate-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { ClimateCardConfig } from '../types/lovelace-mushroom/cards/climate-card-config';
import AbstractCard from './AbstractCard';
/**
* Climate Card Class
*
* Used to create a card for controlling an entity of the climate domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the climate domain.
*/
class ClimateCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {ClimateCardConfig}
* @private
*/
#defaultConfig: ClimateCardConfig = {
type: "custom:mushroom-climate-card",
icon: undefined,
hvac_modes: [
"off",
"cool",
"heat",
"fan_only",
],
show_temperature_control: true,
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): ClimateCardConfig {
return {
type: 'custom:mushroom-climate-card',
icon: undefined,
hvac_modes: ['off', 'cool', 'heat', 'fan_only'],
show_temperature_control: true,
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.ClimateCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {ClimateCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.ClimateCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: ClimateCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...ClimateCard.getDefaultConfig(), ...customConfiguration };
}
}
export {ClimateCard};
export default ClimateCard;

View File

@ -1,102 +0,0 @@
import {cards} from "../types/strategy/cards";
import {StackCardConfig} from "../types/homeassistant/lovelace/cards/types";
import {LovelaceCardConfig} from "../types/homeassistant/data/lovelace";
import {HassServiceTarget} from "home-assistant-js-websocket";
/**
* Controller Card class.
*
* Used for creating a Title Card with controls.
*
* @class
*/
class ControllerCard {
/**
* @type {HassServiceTarget} The target to control the entities of.
* @private
*/
readonly #target: HassServiceTarget;
/**
* Default configuration of the card.
*
* @type {cards.ControllerCardConfig}
* @private
*/
readonly #defaultConfig: cards.ControllerCardConfig = {
type: "mushroom-title-card",
showControls: true,
iconOn: "mdi:power-on",
iconOff: "mdi:power-off",
onService: "none",
offService: "none",
};
/**
* Class constructor.
*
* @param {HassServiceTarget} target The target to control the entities of.
* @param {cards.ControllerCardOptions} options Controller Card options.
*/
constructor(target: HassServiceTarget, options: cards.ControllerCardOptions = {}) {
this.#target = target;
this.#defaultConfig = {
...this.#defaultConfig,
...options,
};
}
/**
* Create a Controller card.
*
* @return {StackCardConfig} A Controller card.
*/
createCard(): StackCardConfig {
const cards: LovelaceCardConfig[] = [
{
type: "custom:mushroom-title-card",
title: this.#defaultConfig.title,
subtitle: this.#defaultConfig.subtitle,
},
];
if (this.#defaultConfig.showControls) {
cards.push({
type: "horizontal-stack",
cards: [
{
type: "custom:mushroom-template-card",
icon: this.#defaultConfig.iconOff,
layout: "vertical",
icon_color: "red",
tap_action: {
action: "call-service",
service: this.#defaultConfig.offService,
target: this.#target,
data: {},
},
},
{
type: "custom:mushroom-template-card",
icon: this.#defaultConfig.iconOn,
layout: "vertical",
icon_color: "amber",
tap_action: {
action: "call-service",
service: this.#defaultConfig.onService,
target: this.#target,
data: {},
},
},
],
});
}
return {
type: "horizontal-stack",
cards: cards,
};
}
}
export {ControllerCard};

View File

@ -1,44 +1,37 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {CoverCardConfig} from "../types/lovelace-mushroom/cards/cover-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { CoverCardConfig } from '../types/lovelace-mushroom/cards/cover-card-config';
import AbstractCard from './AbstractCard';
/**
* Cover Card Class
*
* Used to create a card for controlling an entity of the cover domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the cover domain.
*/
class CoverCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {CoverCardConfig}
* @private
*/
#defaultConfig: CoverCardConfig = {
type: "custom:mushroom-cover-card",
icon: undefined,
show_buttons_control: true,
show_position_control: true,
show_tilt_position_control: true,
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): CoverCardConfig {
return {
type: 'custom:mushroom-cover-card',
icon: undefined,
show_buttons_control: true,
show_position_control: true,
show_tilt_position_control: true,
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.CoverCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {CoverCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.CoverCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: CoverCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...CoverCard.getDefaultConfig(), ...customConfiguration };
}
}
export {CoverCard};
export default CoverCard;

View File

@ -1,44 +1,37 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {FanCardConfig} from "../types/lovelace-mushroom/cards/fan-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { FanCardConfig } from '../types/lovelace-mushroom/cards/fan-card-config';
import AbstractCard from './AbstractCard';
/**
* Fan Card Class
*
* Used to create a card for controlling an entity of the fan domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the fan domain.
*/
class FanCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {FanCardConfig}
* @private
*/
#defaultConfig: FanCardConfig = {
type: "custom:mushroom-fan-card",
icon: undefined,
show_percentage_control: true,
show_oscillate_control: true,
icon_animation: true,
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): FanCardConfig {
return {
type: 'custom:mushroom-fan-card',
icon: undefined,
show_percentage_control: true,
show_oscillate_control: true,
icon_animation: true,
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.FanCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {FanCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.FanCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: FanCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...FanCard.getDefaultConfig(), ...customConfiguration };
}
}
export {FanCard};
export default FanCard;

View File

@ -1,49 +1,45 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {AreaRegistryEntry} from "../types/homeassistant/data/area_registry";
import {AreaCardConfig} from "../types/homeassistant/lovelace/cards/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { AreaRegistryEntry } from '../types/homeassistant/data/area_registry';
import { AreaCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
import AbstractCard from './AbstractCard';
/**
* HA Area Card Class
*
* Used to create a card for an entity of the area domain using the built-in type 'area'.
*
* @class
* @extends AbstractCard
* Used to create card configuration for an entry of the HASS area registry.
*/
class AreaCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {AreaCardConfig}
* @private
*/
#defaultConfig: AreaCardConfig = {
type: "area",
area: "",
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): AreaCardConfig {
return {
type: 'area',
area: '',
};
}
/**
* Class constructor.
*
* @param {AreaRegistryEntry} area The area entity to create a card for.
* @param {cards.AreaCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {AreaRegistryEntry} area The HASS entity to create a card configuration for.
* @param {AreaCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(area: AreaRegistryEntry, options: cards.AreaCardOptions = {}) {
constructor(area: AreaRegistryEntry, customConfiguration?: AreaCardConfig) {
super(area);
// Initialize the default configuration.
this.#defaultConfig.area = area.area_id;
this.#defaultConfig.navigation_path = this.#defaultConfig.area;
const configuration = AreaCard.getDefaultConfig();
// Enforce the card type.
delete options.type;
configuration.area = area.area_id;
configuration.navigation_path = configuration.area;
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = {
...this.configuration,
...configuration,
...customConfiguration,
type: configuration.type, // Enforce the card type.
};
}
}
export {AreaCard};
export default AreaCard;

101
src/cards/HeaderCard.ts Normal file
View File

@ -0,0 +1,101 @@
import { HassServiceTarget } from 'home-assistant-js-websocket';
import { LovelaceCardConfig } from '../types/homeassistant/data/lovelace/config/card';
import { StackCardConfig } from '../types/homeassistant/panels/lovelace/cards/types';
import { CustomHeaderCardConfig, StrategyHeaderCardConfig } from '../types/strategy/strategy-cards';
/**
* Header Card class.
*
* Used to create a card configuration for a Header Card.
* The card can be used to describe a group of cards and optionally to control multiple entities.
*/
class HeaderCard {
/** The target to control the entities of. */
private readonly target: HassServiceTarget;
/** The current configuration of the card after instantiating this class. */
private readonly configuration: StrategyHeaderCardConfig;
/** Returns the default configuration object for the card. */
static getDefaultConfig(): StrategyHeaderCardConfig {
return {
type: 'custom:mushroom-title-card',
showControls: true,
iconOn: 'mdi:power-on',
iconOff: 'mdi:power-off',
onService: 'none',
offService: 'none',
};
}
/**
* Class constructor.
*
* @param {HassServiceTarget} target The target which is optionally controlled by the card.
* @param {CustomHeaderCardConfig} [customConfiguration] Custom card configuration.
*
* @remarks
* The target object can contain one or multiple ids of different entry types.
*/
constructor(target: HassServiceTarget, customConfiguration?: CustomHeaderCardConfig) {
this.target = target;
this.configuration = { ...HeaderCard.getDefaultConfig(), ...customConfiguration };
}
/**
* Create a Header card configuration.
*
* @remarks
* The card is represented by a horizontal stack of cards.
* One title card and optionally two template cards to control entities.
*/
createCard(): StackCardConfig {
// Create a title card.
const cards: LovelaceCardConfig[] = [
{
type: 'custom:mushroom-title-card',
title: this.configuration.title,
subtitle: this.configuration.subtitle,
},
];
// Add controls to the card.
if (this.configuration.showControls) {
cards.push({
type: 'horizontal-stack',
cards: [
{
type: 'custom:mushroom-template-card',
icon: this.configuration.iconOff,
layout: 'vertical',
icon_color: 'red',
tap_action: {
action: 'call-service',
service: this.configuration.offService,
target: this.target,
data: {},
},
},
{
type: 'custom:mushroom-template-card',
icon: this.configuration.iconOn,
layout: 'vertical',
icon_color: 'amber',
tap_action: {
action: 'call-service',
service: this.configuration.onService,
target: this.target,
data: {},
},
},
],
});
}
return {
type: 'horizontal-stack',
cards: cards,
};
}
}
export default HeaderCard;

View File

@ -1,41 +1,34 @@
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {SelectCardConfig} from "../types/lovelace-mushroom/cards/select-card-config";
import {SelectCard} from './SelectCard';
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { SelectCardConfig } from '../types/lovelace-mushroom/cards/select-card-config';
import SelectCard from './SelectCard';
// noinspection JSUnusedGlobalSymbols Class is dynamically imported
/**
* InputSelect Card Class
*
* Used to create a card for controlling an entity of the input_select domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the input_select domain.
*/
class InputSelectCard extends SelectCard {
/**
* Default configuration of the card.
*
* @type {SelectCardConfig}
* @private
*/
#defaultConfig: SelectCardConfig = {
type: "custom:mushroom-select-card",
icon: undefined,
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): SelectCardConfig {
return {
type: 'custom:mushroom-select-card',
icon: undefined,
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.InputSelectCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {SelectCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.InputSelectCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: SelectCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...InputSelectCard.getDefaultConfig(), ...customConfiguration };
}
}
export {InputSelectCard};
export default InputSelectCard;

View File

@ -1,67 +1,55 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {LightCardConfig} from "../types/lovelace-mushroom/cards/light-card-config";
import {generic} from "../types/strategy/generic";
import isCallServiceActionConfig = generic.isCallServiceActionConfig;
import isCallServiceActionTarget = generic.isCallServiceActionTarget;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { LightCardConfig } from '../types/lovelace-mushroom/cards/light-card-config';
import { isCallServiceActionConfig } from '../types/strategy/strategy-generics';
import AbstractCard from './AbstractCard';
/**
* Light Card Class
*
* Used to create a card for controlling an entity of the light domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the light domain.
*/
class LightCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {LightCardConfig}
* @private
*/
#defaultConfig: LightCardConfig = {
type: "custom:mushroom-light-card",
icon: undefined,
show_brightness_control: true,
show_color_control: true,
show_color_temp_control: true,
use_light_color: true,
double_tap_action: {
action: "call-service",
service: "light.turn_on",
target: {
entity_id: undefined,
/** Returns the default configuration object for the card. */
static getDefaultConfig(): LightCardConfig {
return {
type: 'custom:mushroom-light-card',
icon: undefined,
show_brightness_control: true,
show_color_control: true,
show_color_temp_control: true,
use_light_color: true,
double_tap_action: {
action: 'call-service',
perform_action: 'light.turn_on',
target: {
entity_id: undefined,
},
data: {
rgb_color: [255, 255, 255],
},
},
data: {
rgb_color: [255, 255, 255],
},
},
};
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.LightCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {LightCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.LightCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: LightCardConfig) {
super(entity);
// Set the target for double-tap action.
if (
isCallServiceActionConfig(this.#defaultConfig.double_tap_action)
&& isCallServiceActionTarget(this.#defaultConfig.double_tap_action.target)
) {
this.#defaultConfig.double_tap_action.target.entity_id = entity.entity_id;
const configuration = LightCard.getDefaultConfig();
if (isCallServiceActionConfig(configuration.double_tap_action)) {
configuration.double_tap_action.target = { entity_id: entity.entity_id };
}
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...configuration, ...customConfiguration };
}
}
export {LightCard};
export default LightCard;

View File

@ -1,41 +1,34 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {LockCardConfig} from "../types/lovelace-mushroom/cards/lock-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { LockCardConfig } from '../types/lovelace-mushroom/cards/lock-card-config';
import AbstractCard from './AbstractCard';
/**
* Lock Card Class
*
* Used to create a card for controlling an entity of the lock domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the lock domain.
*/
class LockCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {LockCardConfig}
* @private
*/
#defaultConfig: LockCardConfig = {
type: "custom:mushroom-lock-card",
icon: undefined,
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): LockCardConfig {
return {
type: 'custom:mushroom-lock-card',
icon: undefined,
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.LockCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {LockCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.LockCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: LockCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...LockCard.getDefaultConfig(), ...customConfiguration };
}
}
export {LockCard};
export default LockCard;

View File

@ -1,51 +1,37 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {MediaPlayerCardConfig} from "../types/lovelace-mushroom/cards/media-player-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { MediaPlayerCardConfig } from '../types/lovelace-mushroom/cards/media-player-card-config';
import AbstractCard from './AbstractCard';
/**
* Mediaplayer Card Class
*
* Used to create a card for controlling an entity of the media_player domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the media_player domain.
*/
class MediaPlayerCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {MediaPlayerCardConfig}
* @private
*/
#defaultConfig: MediaPlayerCardConfig = {
type: "custom:mushroom-media-player-card",
use_media_info: true,
media_controls: [
"on_off",
"play_pause_stop",
],
show_volume_level: true,
volume_controls: [
"volume_mute",
"volume_set",
"volume_buttons",
],
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): MediaPlayerCardConfig {
return {
type: 'custom:mushroom-media-player-card',
use_media_info: true,
media_controls: ['on_off', 'play_pause_stop'],
show_volume_level: true,
volume_controls: ['volume_mute', 'volume_set', 'volume_buttons'],
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.MediaPlayerCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {MediaPlayerCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.MediaPlayerCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: MediaPlayerCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...MediaPlayerCard.getDefaultConfig(), ...customConfiguration };
}
}
export {MediaPlayerCard};
export default MediaPlayerCard;

View File

@ -1,40 +1,32 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {EntityCardConfig} from "../types/lovelace-mushroom/cards/entity-card-config";
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { EntityCardConfig } from '../types/lovelace-mushroom/cards/entity-card-config';
import AbstractCard from './AbstractCard';
/**
* Miscellaneous Card Class
*
* Used to create a card an entity of any domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of any domain.
*/
class MiscellaneousCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {EntityCardConfig}
* @private
*/
#defaultConfig: EntityCardConfig = {
type: "custom:mushroom-entity-card",
icon_color: "blue-grey",
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): EntityCardConfig {
return {
type: 'custom:mushroom-entity-card',
icon_color: 'blue-grey',
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.EntityCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {EntityCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.EntityCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: EntityCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...MiscellaneousCard.getDefaultConfig(), ...customConfiguration };
}
}
export {MiscellaneousCard};
export default MiscellaneousCard;

View File

@ -1,41 +1,34 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {NumberCardConfig} from "../types/lovelace-mushroom/cards/number-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { NumberCardConfig } from '../types/lovelace-mushroom/cards/number-card-config';
import AbstractCard from './AbstractCard';
// noinspection JSUnusedGlobalSymbols Class is dynamically imported
/**
* Number Card Class
*
* Used to create a card for controlling an entity of the number domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the number domain.
*/
class NumberCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {NumberCardConfig}
* @private
*/
#defaultConfig: NumberCardConfig = {
type: "custom:mushroom-number-card",
icon: undefined,
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): NumberCardConfig {
return {
type: 'custom:mushroom-number-card',
icon: undefined,
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.NumberCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {NumberCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.NumberCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: NumberCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...NumberCard.getDefaultConfig(), ...customConfiguration };
}
}
export {NumberCard};
export default NumberCard;

View File

@ -1,43 +1,35 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {PersonCardConfig} from "../types/lovelace-mushroom/cards/person-card-config";
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { PersonCardConfig } from '../types/lovelace-mushroom/cards/person-card-config';
import AbstractCard from './AbstractCard';
/**
* Person Card Class
*
* Used to create a card for an entity of the Person domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the person domain.
*/
class PersonCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {PersonCardConfig}
* @private
*/
#defaultConfig: PersonCardConfig = {
type: "custom:mushroom-person-card",
layout: "vertical",
primary_info: "none",
secondary_info: "none",
icon_type: "entity-picture",
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): PersonCardConfig {
return {
type: 'custom:mushroom-person-card',
layout: 'vertical',
primary_info: 'none',
secondary_info: 'none',
icon_type: 'entity-picture',
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.PersonCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {PersonCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.PersonCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: PersonCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...PersonCard.getDefaultConfig(), ...customConfiguration };
}
}
export {PersonCard};
export default PersonCard;

View File

@ -1,63 +1,63 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {generic} from "../types/strategy/generic";
import {EntityCardConfig} from "../types/lovelace-mushroom/cards/entity-card-config";
import {Helper} from "../Helper";
import isCallServiceActionConfig = generic.isCallServiceActionConfig;
import isCallServiceActionTarget = generic.isCallServiceActionTarget;
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { EntityCardConfig } from '../types/lovelace-mushroom/cards/entity-card-config';
import AbstractCard from './AbstractCard';
import SwitchCard from './SwitchCard';
import { isCallServiceActionConfig } from '../types/strategy/strategy-generics';
/**
* Scene Card Class
*
* Used to create a card for an entity of the scene domain.
* Used to create a card configuration to control an entity of the scene domain.
*
* @class
* @extends AbstractCard
* Supports Stateful scenes from https://github.com/hugobloem/stateful_scenes.
* If the stateful scene entity is available, it will be used instead of the original scene entity.
*/
class SceneCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {EntityCardConfig}
* @private
*/
#defaultConfig: EntityCardConfig = {
type: "custom:mushroom-entity-card",
icon: "mdi:palette",
icon_color: "blue",
tap_action: {
action: "call-service",
service: "scene.turn_on",
target: {
entity_id: undefined,
/** Returns the default configuration object for the card. */
static getDefaultConfig(): EntityCardConfig {
return {
type: 'custom:mushroom-entity-card',
tap_action: {
action: 'perform-action',
perform_action: 'scene.turn_on',
target: {},
},
},
};
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.EntityCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {EntityCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.EntityCardOptions = {}) {
super(entity);
constructor(entity: EntityRegistryEntry, customConfiguration?: EntityCardConfig) {
const sceneName = entity.entity_id.split('.').pop();
const statefulScene = Registry.entities.find((entity) => entity.entity_id === `switch.${sceneName}_stateful_scene`);
// Set the target for tap action.
if (
isCallServiceActionConfig(this.#defaultConfig.tap_action)
&& isCallServiceActionTarget(this.#defaultConfig.tap_action.target)
) {
this.#defaultConfig.tap_action.target.entity_id = entity.entity_id;
super(statefulScene ?? entity);
// Stateful scene support.
if (statefulScene) {
this.configuration = new SwitchCard(statefulScene).getCard();
return;
}
this.#defaultConfig.icon = Helper.getEntityState(entity)?.attributes.icon ?? this.#defaultConfig.icon;
// Initialize the default configuration.
const configuration = SceneCard.getDefaultConfig();
this.config = Object.assign(this.config, this.#defaultConfig, options);
if (isCallServiceActionConfig(configuration.tap_action)) {
configuration.tap_action.target = { entity_id: entity.entity_id };
}
configuration.icon = Registry.hassStates[entity.entity_id]?.attributes.icon ?? configuration.icon;
this.configuration = { ...this.configuration, ...configuration, ...customConfiguration };
}
}
export {SceneCard};
export default SceneCard;

View File

@ -1,41 +1,34 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {SelectCardConfig} from "../types/lovelace-mushroom/cards/select-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { SelectCardConfig } from '../types/lovelace-mushroom/cards/select-card-config';
import AbstractCard from './AbstractCard';
// noinspection JSUnusedGlobalSymbols Class is dynamically imported
/**
* Select Card Class
*
* Used to create a card for controlling an entity of the select domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the select domain.
*/
class SelectCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {SelectCardConfig}
* @private
*/
#defaultConfig: SelectCardConfig = {
type: "custom:mushroom-select-card",
icon: undefined,
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): SelectCardConfig {
return {
type: 'custom:mushroom-select-card',
icon: undefined,
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.SelectCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {SelectCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.SelectCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: SelectCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...SelectCard.getDefaultConfig(), ...customConfiguration };
}
}
export {SelectCard};
export default SelectCard;

View File

@ -1,42 +1,34 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {EntityCardConfig} from "../types/lovelace-mushroom/cards/entity-card-config";
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { EntityCardConfig } from '../types/lovelace-mushroom/cards/entity-card-config';
import AbstractCard from './AbstractCard';
/**
* Sensor Card Class
*
* Used to create a card for controlling an entity of the sensor domain.
*
* @class
* @extends AbstractCard
*/
class SensorCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {EntityCardConfig}
* @private
*/
#defaultConfig: EntityCardConfig = {
type: "custom:mushroom-entity-card",
icon: "mdi:information",
animate: true,
line_color: "green",
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): EntityCardConfig {
return {
type: 'custom:mushroom-entity-card',
icon: 'mdi:information',
animate: true,
line_color: 'green',
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.EntityCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {EntityCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.EntityCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: EntityCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...SensorCard.getDefaultConfig(), ...customConfiguration };
}
}
export {SensorCard};
export default SensorCard;

View File

@ -1,44 +1,37 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {EntityCardConfig} from "../types/lovelace-mushroom/cards/entity-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { EntityCardConfig } from '../types/lovelace-mushroom/cards/entity-card-config';
import AbstractCard from './AbstractCard';
/**
* Switch Card Class
*
* Used to create a card for controlling an entity of the switch domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the switch domain.
*/
class SwitchCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {EntityCardConfig}
* @private
*/
#defaultConfig: EntityCardConfig = {
type: "custom:mushroom-entity-card",
icon: undefined,
tap_action: {
action: "toggle",
},
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): EntityCardConfig {
return {
type: 'custom:mushroom-entity-card',
icon: undefined,
tap_action: {
action: 'toggle',
},
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.EntityCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {EntityCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.EntityCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: EntityCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...SwitchCard.getDefaultConfig(), ...customConfiguration };
}
}
export {SwitchCard};
export default SwitchCard;

View File

@ -1,46 +1,39 @@
import {AbstractCard} from "./AbstractCard";
import {cards} from "../types/strategy/cards";
import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry";
import {VACUUM_COMMANDS, VacuumCardConfig} from "../types/lovelace-mushroom/cards/vacuum-card-config";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { VACUUM_COMMANDS, VacuumCardConfig } from '../types/lovelace-mushroom/cards/vacuum-card-config';
import AbstractCard from './AbstractCard';
/**
* Vacuum Card Class
*
* Used to create a card for controlling an entity of the vacuum domain.
*
* @class
* @extends AbstractCard
* Used to create a card configuration to control an entity of the vacuum domain.
*/
class VacuumCard extends AbstractCard {
/**
* Default configuration of the card.
*
* @type {VacuumCardConfig}
* @private
*/
#defaultConfig: VacuumCardConfig = {
type: "custom:mushroom-vacuum-card",
icon: undefined,
icon_animation: true,
commands: [...VACUUM_COMMANDS],
tap_action: {
action: "more-info",
}
};
/** Returns the default configuration object for the card. */
static getDefaultConfig(): VacuumCardConfig {
return {
type: 'custom:mushroom-vacuum-card',
icon: undefined,
icon_animation: true,
commands: [...VACUUM_COMMANDS],
tap_action: {
action: 'more-info',
},
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The hass entity to create a card for.
* @param {cards.VacuumCardOptions} [options={}] Options for the card.
* @throws {Error} If the Helper module isn't initialized.
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {VacuumCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, options: cards.VacuumCardOptions = {}) {
constructor(entity: EntityRegistryEntry, customConfiguration?: VacuumCardConfig) {
super(entity);
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...VacuumCard.getDefaultConfig(), ...customConfiguration };
}
}
export {VacuumCard};
export default VacuumCard;

57
src/cards/ValveCard.ts Normal file
View File

@ -0,0 +1,57 @@
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { EntityRegistryEntry } from '../types/homeassistant/data/entity_registry';
import { TemplateCardConfig } from '../types/lovelace-mushroom/cards/template-card-config';
import { localize } from '../utilities/localize';
import AbstractCard from './AbstractCard';
/**
* Valve Card Class
*
* Used to create a card configuration to control an entity of the valve domain.
*/
class ValveCard extends AbstractCard {
/** Returns the default configuration object for the card. */
static getDefaultConfig(): TemplateCardConfig {
return {
type: 'custom:mushroom-template-card',
icon: 'mdi:valve',
icon_color: 'blue',
double_tap_action: {
action: 'toggle',
},
};
}
/**
* Class constructor.
*
* @param {EntityRegistryEntry} entity The HASS entity to create a card configuration for.
* @param {VacuumCardConfig} [customConfiguration] Custom card configuration.
*/
constructor(entity: EntityRegistryEntry, customConfiguration?: TemplateCardConfig) {
super(entity);
// Initialize the default configuration.
const configuration = ValveCard.getDefaultConfig();
configuration.entity = entity.entity_id;
configuration.icon = entity.icon ?? configuration.icon;
configuration.primary = entity.name ?? entity.original_name ?? '?';
configuration.secondary = `{%
set mapping = {
'open': '${localize('valve.open')}',
'opening': '${localize('valve.opening')}',
'closed': '${localize('valve.closed')}',
'closing': '${localize('valve.closing')}',
'stopped': '${localize('valve.stopped')}',
'unavailable': '${localize('generic.unavailable')}'
}
%}
{{ mapping.get(states('${entity.entity_id}'), '${localize('generic.unknown')}') }}`;
this.configuration = { ...this.configuration, ...configuration, ...customConfiguration };
}
}
export default ValveCard;

View File

@ -1,64 +1,48 @@
import {HassServiceTarget} from "home-assistant-js-websocket";
import {LovelaceChipConfig} from "../types/lovelace-mushroom/utils/lovelace/chip/types";
import {Helper} from "../Helper";
import {generic} from "../types/strategy/generic";
import isCallServiceActionConfig = generic.isCallServiceActionConfig;
import { Registry } from '../Registry';
import { LovelaceChipConfig } from '../types/lovelace-mushroom/utils/lovelace/chip/types';
import { logMessage, lvlFatal } from '../utilities/debug';
/**
* Abstract Chip class.
*
* To create a new chip, extend this one.
*
* @class
* @abstract
*/
abstract class AbstractChip {
/**
* Abstract Chip class.
*
* To create a chip configuration, this class should be extended by a child class.
* Child classes should override the default configuration so the chip correctly reflects the entity.
*
* @remarks
* Before using this class, the Registry module must be initialized by calling {@link Registry.initialize}.
*/
/**
* Configuration of the chip.
*
* @type {LovelaceChipConfig}
* Child classes should override this property to reflect their own card type and options.
*/
config: LovelaceChipConfig = {
type: "template"
protected configuration: LovelaceChipConfig = {
// TODO: Check if this is correct vs custom:mushroom-template-badge. Also in child classes.
type: 'template',
};
/**
* Class Constructor.
*
* @remarks
* Before using this class, the Registry module must be initialized by calling {@link Registry.initialize}.
*/
protected constructor() {
if (!Helper.isInitialized()) {
throw new Error("The Helper module must be initialized before using this one.");
if (!Registry.initialized) {
logMessage(lvlFatal, 'Registry not initialized!');
}
}
// noinspection JSUnusedGlobalSymbols Method is called on dymanically imported classes.
/**
* Get the chip.
* Get a chip configuration.
*
* @returns {LovelaceChipConfig} A chip.
* The configuration should be set by any of the child classes so the chip correctly reflects an entity.
*/
getChip(): LovelaceChipConfig {
return this.config;
}
/**
* Set the target to switch.
*
* @param {HassServiceTarget} target Target to switch.
*/
setTapActionTarget(target: HassServiceTarget) {
if ("tap_action" in this.config && isCallServiceActionConfig(this.config.tap_action)) {
this.config.tap_action.target = target;
return;
}
if (Helper.debug) {
console.warn(
this.constructor.name
+ " - Target not set: Invalid target or tap action.");
}
getChipConfiguration(): LovelaceChipConfig {
return this.configuration;
}
}
export {AbstractChip};
export default AbstractChip;

View File

@ -1,47 +1,42 @@
import {Helper} from "../Helper";
import {AbstractChip} from "./AbstractChip";
import {chips} from "../types/strategy/chips";
import {TemplateChipConfig} from "../types/lovelace-mushroom/utils/lovelace/chip/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { TemplateChipConfig } from '../types/lovelace-mushroom/utils/lovelace/chip/types';
import AbstractChip from './AbstractChip';
/**
* Climate Chip class.
*
* Used to create a chip to indicate how many climates are operating.
* Used to create a chip configuration to indicate how many climates are operating.
*/
class ClimateChip extends AbstractChip {
/**
* Default configuration of the chip.
*
* @type {TemplateChipConfig}
*
* @readonly
* @private
*/
readonly #defaultConfig: TemplateChipConfig = {
type: "template",
icon: "mdi:thermostat",
icon_color: "orange",
content: Helper.getCountTemplate("climate", "ne", "off"),
tap_action: {
action: "none",
},
hold_action: {
action: "navigate",
navigation_path: "climates",
},
};
/** Returns the default configuration object for the chip. */
static getDefaultConfig(): TemplateChipConfig {
return {
type: 'template',
icon: 'mdi:thermostat',
icon_color: 'orange',
content: Registry.getCountTemplate('climate', 'ne', 'off'),
tap_action: {
action: 'none',
},
hold_action: {
action: 'navigate',
navigation_path: 'climates',
},
};
}
/**
* Class Constructor.
*
* @param {chips.TemplateChipOptions} options The chip options.
* @param {TemplateChipConfig} [customConfiguration] Custom chip configuration.
*/
constructor(options: chips.TemplateChipOptions = {}) {
constructor(customConfiguration?: TemplateChipConfig) {
super();
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...ClimateChip.getDefaultConfig(), ...customConfiguration };
}
}
export {ClimateChip};
export default ClimateChip;

View File

@ -1,47 +1,42 @@
import {Helper} from "../Helper";
import {chips} from "../types/strategy/chips";
import {AbstractChip} from "./AbstractChip";
import {TemplateChipConfig} from "../types/lovelace-mushroom/utils/lovelace/chip/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { TemplateChipConfig } from '../types/lovelace-mushroom/utils/lovelace/chip/types';
import AbstractChip from './AbstractChip';
/**
* Cover Chip class.
*
* Used to create a chip to indicate how many covers aren't closed.
* Used to create a chip configuration to indicate how many covers aren't closed.
*/
class CoverChip extends AbstractChip {
/**
* Default configuration of the chip.
*
* @type {TemplateChipConfig}
*
* @readonly
* @private
*/
readonly #defaultConfig: TemplateChipConfig = {
type: "template",
icon: "mdi:window-open",
icon_color: "cyan",
content: Helper.getCountTemplate("cover", "search", "(open|opening|closing)"),
tap_action: {
action: "none",
},
hold_action: {
action: "navigate",
navigation_path: "covers",
},
};
/** Returns the default configuration object for the chip. */
static getDefaultConfig(): TemplateChipConfig {
return {
type: 'template',
icon: 'mdi:window-open',
icon_color: 'cyan',
content: Registry.getCountTemplate('cover', 'search', '(open|opening|closing)'),
tap_action: {
action: 'none',
},
hold_action: {
action: 'navigate',
navigation_path: 'covers',
},
};
}
/**
* Class Constructor.
*
* @param {chips.TemplateChipOptions} options The chip options.
* @param {TemplateChipConfig} [customConfiguration] Custom chip configuration.
*/
constructor(options: chips.TemplateChipOptions = {}) {
constructor(customConfiguration?: TemplateChipConfig) {
super();
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...CoverChip.getDefaultConfig(), ...customConfiguration };
}
}
export {CoverChip};
export default CoverChip;

View File

@ -1,48 +1,49 @@
import {Helper} from "../Helper";
import {chips} from "../types/strategy/chips";
import {AbstractChip} from "./AbstractChip";
import {TemplateChipConfig} from "../types/lovelace-mushroom/utils/lovelace/chip/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { TemplateChipConfig } from '../types/lovelace-mushroom/utils/lovelace/chip/types';
import AbstractChip from './AbstractChip';
import RegistryFilter from '../utilities/RegistryFilter';
/**
* Fan Chip class.
*
* Used to create a chip to indicate how many fans are on and to turn all off.
* Used to create a chip to indicate how many fans are on and to switch them all off.
*/
class FanChip extends AbstractChip {
/**
* Default configuration of the chip.
*
* @type {TemplateChipConfig}
*
* @readonly
* @private
*/
readonly #defaultConfig: TemplateChipConfig = {
type: "template",
icon: "mdi:fan",
icon_color: "green",
content: Helper.getCountTemplate("fan", "eq", "on"),
tap_action: {
action: "call-service",
service: "fan.turn_off",
},
hold_action: {
action: "navigate",
navigation_path: "fans",
},
};
/** Returns the default configuration object for the chip. */
static getDefaultConfig(): TemplateChipConfig {
return {
type: 'template',
icon: 'mdi:fan',
icon_color: 'green',
content: Registry.getCountTemplate('fan', 'eq', 'on'),
tap_action: {
action: 'perform-action',
perform_action: 'fan.turn_off',
target: {
entity_id: new RegistryFilter(Registry.entities)
.whereDomain('fan')
.getValuesByProperty('entity_id') as string[],
},
},
hold_action: {
action: 'navigate',
navigation_path: 'fans',
},
};
}
/**
* Class Constructor.
*
* @param {chips.TemplateChipOptions} options The chip options.
* @param {TemplateChipConfig} [customConfiguration] Custom chip configuration.
*/
constructor(options: chips.TemplateChipOptions = {}) {
constructor(customConfiguration?: TemplateChipConfig) {
super();
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...FanChip.getDefaultConfig(), ...customConfiguration };
}
}
export {FanChip};
export default FanChip;

View File

@ -1,48 +1,49 @@
import {Helper} from "../Helper";
import {chips} from "../types/strategy/chips";
import {AbstractChip} from "./AbstractChip";
import {TemplateChipConfig} from "../types/lovelace-mushroom/utils/lovelace/chip/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { TemplateChipConfig } from '../types/lovelace-mushroom/utils/lovelace/chip/types';
import AbstractChip from './AbstractChip';
import RegistryFilter from '../utilities/RegistryFilter';
/**
* Light Chip class.
*
* Used to create a chip to indicate how many lights are on and to turn all off.
* Used to create a chip configuration to indicate how many lights are on and to switch them all off.
*/
class LightChip extends AbstractChip {
/**
* Default configuration of the chip.
*
* @type {TemplateChipConfig}
*
* @readonly
* @private
*/
readonly #defaultConfig: TemplateChipConfig = {
type: "template",
icon: "mdi:lightbulb-group",
icon_color: "amber",
content: Helper.getCountTemplate("light", "eq", "on"),
tap_action: {
action: "call-service",
service: "light.turn_off",
},
hold_action: {
action: "navigate",
navigation_path: "lights",
},
};
/** Returns the default configuration object for the chip. */
static getDefaultConfig(): TemplateChipConfig {
return {
type: 'template',
icon: 'mdi:lightbulb-group',
icon_color: 'amber',
content: Registry.getCountTemplate('light', 'eq', 'on'),
tap_action: {
action: 'perform-action',
perform_action: 'light.turn_off',
target: {
entity_id: new RegistryFilter(Registry.entities)
.whereDomain('light')
.getValuesByProperty('entity_id') as string[],
},
},
hold_action: {
action: 'navigate',
navigation_path: 'lights',
},
};
}
/**
* Class Constructor.
*
* @param {chips.TemplateChipOptions} options The chip options.
* @param {TemplateChipConfig} [customConfiguration] Custom chip configuration.
*/
constructor(options: chips.TemplateChipOptions = {}) {
constructor(customConfiguration?: TemplateChipConfig) {
super();
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...LightChip.getDefaultConfig(), ...customConfiguration };
}
}
export {LightChip};
export default LightChip;

View File

@ -1,48 +1,49 @@
import {Helper} from "../Helper";
import {chips} from "../types/strategy/chips";
import {AbstractChip} from "./AbstractChip";
import {TemplateChipConfig} from "../types/lovelace-mushroom/utils/lovelace/chip/types";
// noinspection JSUnusedGlobalSymbols Class is dynamically imported.
import { Registry } from '../Registry';
import { TemplateChipConfig } from '../types/lovelace-mushroom/utils/lovelace/chip/types';
import AbstractChip from './AbstractChip';
import RegistryFilter from '../utilities/RegistryFilter';
/**
* Switch Chip class.
*
* Used to create a chip to indicate how many switches are on and to turn all off.
* Used to create a chip configuration to indicate how many switches are on and to switch them all off.
*/
class SwitchChip extends AbstractChip {
/**
* Default configuration of the chip.
*
* @type {TemplateChipConfig}
*
* @readonly
* @private
*/
readonly #defaultConfig: TemplateChipConfig = {
type: "template",
icon: "mdi:dip-switch",
icon_color: "blue",
content: Helper.getCountTemplate("switch", "eq", "on"),
tap_action: {
action: "call-service",
service: "switch.turn_off",
},
hold_action: {
action: "navigate",
navigation_path: "switches",
},
};
/** Returns the default configuration object for the chip. */
static getDefaultConfig(): TemplateChipConfig {
return {
type: 'template',
icon: 'mdi:dip-switch',
icon_color: 'blue',
content: Registry.getCountTemplate('switch', 'eq', 'on'),
tap_action: {
action: 'perform-action',
perform_action: 'switch.turn_off',
target: {
entity_id: new RegistryFilter(Registry.entities)
.whereDomain('switch')
.getValuesByProperty('entity_id') as string[],
},
},
hold_action: {
action: 'navigate',
navigation_path: 'switches',
},
};
}
/**
* Class Constructor.
*
* @param {chips.TemplateChipOptions} options The chip options.
* @param {TemplateChipConfig} [customConfiguration] Custom chip configuration.
*/
constructor(options: chips.TemplateChipOptions = {}) {
constructor(customConfiguration?: TemplateChipConfig) {
super();
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...SwitchChip.getDefaultConfig(), ...customConfiguration };
}
}
export {SwitchChip};
export default SwitchChip;

View File

@ -1,42 +1,35 @@
import {chips} from "../types/strategy/chips";
import {WeatherChipConfig} from "../types/lovelace-mushroom/utils/lovelace/chip/types";
import {AbstractChip} from "./AbstractChip";
// noinspection JSUnusedGlobalSymbols False positive.
import { WeatherChipConfig } from '../types/lovelace-mushroom/utils/lovelace/chip/types';
import AbstractChip from './AbstractChip';
/**
* Weather Chip class.
*
* Used to create a chip for showing the weather.
* Used to create a chip configuration to indicate the current weather.
*/
class WeatherChip extends AbstractChip {
/**
* Default configuration of the chip.
*
* @private
* @readonly
*/
readonly #defaultConfig: WeatherChipConfig = {
type: "weather",
show_temperature: true,
show_conditions: true,
};
/** Returns the default configuration object for the chip. */
static getDefaultConfig(entityId: string): WeatherChipConfig {
return {
type: 'weather',
entity: entityId,
show_temperature: true,
show_conditions: true,
};
}
/**
* Class Constructor.
*
* @param {string} entityId Id of a weather entity.
* @param {chips.WeatherChipOptions} options Weather Chip options.
* @param {WeatherChipConfig} [customConfiguration] Custom chip configuration.
*/
constructor(entityId: string, options: chips.WeatherChipOptions = {}) {
constructor(entityId: string, customConfiguration?: WeatherChipConfig) {
super();
this.#defaultConfig = {
...this.#defaultConfig,
...{entity: entityId},
...options,
};
this.config = Object.assign(this.config, this.#defaultConfig, options);
this.configuration = { ...this.configuration, ...WeatherChip.getDefaultConfig(entityId), ...customConfiguration };
}
}
export {WeatherChip};
export default WeatherChip;

View File

@ -1,167 +1,188 @@
import {generic} from "./types/strategy/generic";
import StrategyDefaults = generic.StrategyDefaults;
import { StrategyDefaults } from './types/strategy/strategy-generics';
import { localize } from './utilities/localize';
/**
* Default configuration for the mushroom strategy.
*/
export const getConfigurationDefaults = (localize: Function): StrategyDefaults => {
return {
areas: {
undisclosed: {
area_id: "undisclosed",
floor_id: null,
name: "Undisclosed",
picture: null,
icon: "mdi:floor-plan",
labels: [],
aliases: [],
hidden: false,
}
export const ConfigurationDefaults: StrategyDefaults = {
areas: {
undisclosed: {
// TODO: Refactor undisclosed to other.
aliases: [],
area_id: 'undisclosed',
created_at: 0,
floor_id: null,
hidden: false,
humidity_entity_id: null,
icon: 'mdi:floor-plan',
labels: [],
modified_at: 0,
name: localize('generic.undisclosed'),
picture: null,
temperature_entity_id: null,
},
debug: false,
domains: {
_: {
hide_config_entities: true,
hide_diagnostic_entities: true,
},
default: {
title: localize("generic.miscellaneous"),
showControls: false,
hidden: false,
},
light: {
title: localize("light.lights"),
showControls: true,
iconOn: "mdi:lightbulb",
iconOff: "mdi:lightbulb-off",
onService: "light.turn_on",
offService: "light.turn_off",
hidden: false,
},
scene: {
title: localize("scene.scenes"),
showControls: false,
onService: "scene.turn_on",
hidden: false,
},
fan: {
title: localize("fan.fans"),
showControls: true,
iconOn: "mdi:fan",
iconOff: "mdi:fan-off",
onService: "fan.turn_on",
offService: "fan.turn_off",
hidden: false,
},
cover: {
title: localize("cover.covers"),
showControls: true,
iconOn: "mdi:arrow-up",
iconOff: "mdi:arrow-down",
onService: "cover.open_cover",
offService: "cover.close_cover",
hidden: false,
},
switch: {
title: localize("switch.switches"),
showControls: true,
iconOn: "mdi:power-plug",
iconOff: "mdi:power-plug-off",
onService: "switch.turn_on",
offService: "switch.turn_off",
hidden: false,
},
camera: {
title: localize("camera.cameras"),
showControls: false,
hidden: false,
},
lock: {
title: localize("lock.locks"),
showControls: false,
hidden: false,
},
climate: {
title: localize("climate.climates"),
showControls: false,
hidden: false,
},
media_player: {
title: localize("media_player.media_players"),
showControls: false,
hidden: false,
},
sensor: {
title: localize("sensor.sensors"),
showControls: false,
hidden: false,
},
binary_sensor: {
title: `${localize("sensor.binary")} ` + localize("sensor.sensors"),
showControls: false,
hidden: false,
},
number: {
title: localize("generic.numbers"),
showControls: false,
hidden: false,
},
vacuum: {
title: localize("vacuum.vacuums"),
showControls: true,
hidden: false,
},
select: {
title: localize("select.selects"),
showControls: false,
hidden: false,
},
input_select: {
title: localize("input_select.input_selects"),
showControls: false,
hidden: false,
},
},
card_options: {},
chips: {
// TODO: Make chips sortable.
weather_entity: 'auto',
light_count: true,
fan_count: true,
cover_count: true,
switch_count: true,
climate_count: true,
extra_chips: [],
},
debug: false,
domains: {
_: {
hide_config_entities: undefined,
hide_diagnostic_entities: undefined,
},
home_view: {
hidden: [],
binary_sensor: {
title: `${localize('sensor.binary')} ` + localize('sensor.sensors'),
showControls: false,
hidden: false,
},
views: {
home: {
order: 1,
hidden: false,
},
light: {
order: 2,
hidden: false,
},
fan: {
order: 3,
hidden: false,
},
cover: {
order: 4,
hidden: false,
},
switch: {
order: 5,
hidden: false,
},
climate: {
order: 6,
hidden: false,
},
camera: {
order: 7,
hidden: false,
},
vacuum: {
order: 8,
hidden: false,
},
scene: {
order: 9,
hidden: false,
},
}
};
camera: {
title: localize('camera.cameras'),
showControls: false,
hidden: false,
},
climate: {
title: localize('climate.climates'),
showControls: false,
hidden: false,
},
cover: {
title: localize('cover.covers'),
showControls: true,
iconOn: 'mdi:arrow-up',
iconOff: 'mdi:arrow-down',
onService: 'cover.open_cover',
offService: 'cover.close_cover',
hidden: false,
},
default: {
title: localize('generic.miscellaneous'),
showControls: false,
hidden: false,
},
fan: {
title: localize('fan.fans'),
showControls: true,
iconOn: 'mdi:fan',
iconOff: 'mdi:fan-off',
onService: 'fan.turn_on',
offService: 'fan.turn_off',
hidden: false,
},
input_select: {
title: localize('input_select.input_selects'),
showControls: false,
hidden: false,
},
light: {
title: localize('light.lights'),
showControls: true,
iconOn: 'mdi:lightbulb',
iconOff: 'mdi:lightbulb-off',
onService: 'light.turn_on',
offService: 'light.turn_off',
hidden: false,
},
lock: {
title: localize('lock.locks'),
showControls: false,
hidden: false,
},
media_player: {
title: localize('media_player.media_players'),
showControls: false,
hidden: false,
},
number: {
title: localize('generic.numbers'),
showControls: false,
hidden: false,
},
scene: {
title: localize('scene.scenes'),
showControls: false,
onService: 'scene.turn_on',
hidden: false,
},
select: {
title: localize('select.selects'),
showControls: false,
hidden: false,
},
sensor: {
title: localize('sensor.sensors'),
showControls: false,
hidden: false,
},
switch: {
title: localize('switch.switches'),
showControls: true,
iconOn: 'mdi:power-plug',
iconOff: 'mdi:power-plug-off',
onService: 'switch.turn_on',
offService: 'switch.turn_off',
hidden: false,
},
vacuum: {
title: localize('vacuum.vacuums'),
showControls: true,
hidden: false,
},
},
extra_cards: [],
extra_views: [],
home_view: {
hidden: [],
},
views: {
camera: {
order: 7,
hidden: false,
},
climate: {
order: 6,
hidden: false,
},
cover: {
order: 4,
hidden: false,
},
fan: {
order: 3,
hidden: false,
},
home: {
order: 1,
hidden: false,
},
light: {
order: 2,
hidden: false,
},
lock: {
order: 10,
hidden: false,
},
scene: {
order: 9,
hidden: false,
},
switch: {
order: 5,
hidden: false,
},
vacuum: {
order: 8,
hidden: false,
},
},
quick_access_cards: [],
};

View File

@ -1,60 +0,0 @@
import {HomeAssistant} from "./types/homeassistant/types";
import * as en from "./translations/en.json";
import * as es from "./translations/es.json";
import * as nl from "./translations/nl.json";
import * as de from "./translations/de.json";
/* Registry of currently supported languages */
const languages: Record<string, unknown> = {
en,
es,
nl,
de,
};
/* The fallback language if the user-defined language isn't defined */
const DEFAULT_LANG = "en";
/**
* Get a string by keyword and language.
*
* @param {string} key The keyword to look for in object notation (E.g. generic.home).
* @param {string} lang The language to get the string from (E.g. en).
*
* @return {string | undefined} The requested string or undefined if the keyword doesn't exist/on error.
*/
function getTranslatedString(key: string, lang: string): string | undefined {
try {
return key
.split(".")
.reduce(
(o, i) => (o as Record<string, unknown>)[i],
languages[lang]
) as string;
} catch (_) {
return undefined;
}
}
/**
* Set up the localization.
*
* It reads the user-defined language with a fall-back to english and returns a function to get strings from
* language-files by keyword.
*
* If the keyword is undefined, or on error, the keyword itself is returned.
*
* @param {HomeAssistant} hass The Home Assistant object.
* @return {(key: string) => string} The function to call for translating strings.
*/
export default function setupCustomLocalize(hass?: HomeAssistant): (key: string) => string {
return function (key: string) {
const lang = hass?.locale.language ?? DEFAULT_LANG;
let translated = getTranslatedString(key, lang);
if (!translated) translated = getTranslatedString(key, DEFAULT_LANG);
return translated ?? key;
};
}

View File

@ -1,268 +1,208 @@
import {Helper} from "./Helper";
import {SensorCard} from "./cards/SensorCard";
import {ControllerCard} from "./cards/ControllerCard";
import {generic} from "./types/strategy/generic";
import {LovelaceCardConfig, LovelaceConfig, LovelaceViewConfig} from "./types/homeassistant/data/lovelace";
import {StackCardConfig} from "./types/homeassistant/lovelace/cards/types";
import {EntityCardConfig} from "./types/lovelace-mushroom/cards/entity-card-config";
import {HassServiceTarget} from "home-assistant-js-websocket";
import {applyEntityCategoryFilters} from "./utillties/filters";
import StrategyArea = generic.StrategyArea;
import { HassServiceTarget } from 'home-assistant-js-websocket';
import HeaderCard from './cards/HeaderCard';
import SensorCard from './cards/SensorCard';
import { Registry } from './Registry';
import { LovelaceCardConfig } from './types/homeassistant/data/lovelace/config/card';
import { LovelaceConfig } from './types/homeassistant/data/lovelace/config/types';
import { LovelaceViewConfig, LovelaceViewRawConfig } from './types/homeassistant/data/lovelace/config/view';
import {
DashboardInfo,
isSupportedDomain,
isSupportedView,
StrategyArea,
ViewInfo,
} from './types/strategy/strategy-generics';
import { sanitizeClassName } from './utilities/auxiliaries';
import { logMessage, lvlError } from './utilities/debug';
import RegistryFilter from './utilities/RegistryFilter';
import { stackHorizontal } from './utilities/cardStacking';
/**
* Mushroom Dashboard Strategy.<br>
* <br>
* Mushroom dashboard strategy provides a strategy for Home-Assistant to create a dashboard automatically.<br>
* The strategy makes use Mushroom and Mini Graph cards to represent your entities.<br>
* <br>
* Features:<br>
* 🛠 Automatically create dashboard with three lines of yaml.<br>
* 😍 Built-in Views for several standard domains.<br>
* 🎨 Many options to customize to your needs.<br>
* <br>
* Check the [Repository]{@link https://github.com/AalianKhan/mushroom-strategy} for more information.
* The strategy makes use Mushroom and Mini Graph cards to represent your entities.
*
* @see https://github.com/DigiLive/mushroom-strategy
*/
class MushroomStrategy extends HTMLTemplateElement {
/**
* Generate a dashboard.
*
* Called when opening a dashboard.
* This method creates views for each exposed domain and area.
* It also adds custom views if specified in the strategy options.
*
* @param {generic.DashBoardInfo} info Dashboard strategy information object.
* @return {Promise<LovelaceConfig>}
* @param {DashboardInfo} info Dashboard strategy information object.
*
* @remarks
* Called when opening a dashboard.
*/
static async generateDashboard(info: generic.DashBoardInfo): Promise<LovelaceConfig> {
await Helper.initialize(info);
static async generateDashboard(info: DashboardInfo): Promise<LovelaceConfig> {
await Registry.initialize(info);
// Create views.
const views: LovelaceViewConfig[] = info.config?.views ?? [];
const views: LovelaceViewRawConfig[] = [];
let viewModule;
// Parallelize view imports and creation.
const viewPromises = Registry.getExposedNames('view')
.filter(isSupportedView)
.map(async (viewName) => {
try {
const moduleName = sanitizeClassName(`${viewName}View`);
const View = (await import(`./views/${moduleName}`)).default;
const currentView = new View(Registry.strategyOptions.views[viewName]);
const viewConfiguration = await currentView.getView();
// Create a view for each exposed domain.
for (let viewId of Helper.getExposedViewIds()) {
try {
const viewType = Helper.sanitizeClassName(viewId + "View");
viewModule = await import(`./views/${viewType}`);
const view: LovelaceViewConfig = await new viewModule[viewType](Helper.strategyOptions.views[viewId]).getView();
if (view.cards?.length) {
views.push(view);
if (viewConfiguration.cards.length) {
return viewConfiguration;
}
} catch (e) {
logMessage(lvlError, `Error importing ${viewName} view!`, e);
}
} catch (e) {
Helper.logError(`View '${viewId}' couldn't be loaded!`, e);
}
return null;
});
const resolvedViews = (await Promise.all(viewPromises)).filter(Boolean) as LovelaceViewRawConfig[];
views.push(...resolvedViews);
// Subviews for areas
views.push(
...Registry.areas.map((area) => ({
title: area.name,
path: area.area_id,
subview: true,
strategy: {
type: 'custom:mushroom-strategy',
options: { area },
},
})),
);
// Extra views
if (Registry.strategyOptions.extra_views) {
views.push(...Registry.strategyOptions.extra_views);
}
// Create subviews for each area.
for (let area of Helper.areas) {
if (!area.hidden) {
views.push({
title: area.name,
path: area.area_id ?? area.name,
subview: true,
strategy: {
type: "custom:mushroom-strategy",
options: {
area,
},
},
});
}
}
// Add custom views.
if (Helper.strategyOptions.extra_views) {
views.push(...Helper.strategyOptions.extra_views);
}
// Return the created views.
return {
views: views,
};
return { views };
}
/**
* Generate a view.
*
* Called when opening a subview.
* The method creates cards for each domain (e.g., sensors, switches, etc.) in the current area, using a combination
* of Header cards and entity-specific cards.
* It also handles miscellaneous entities that don't fit into any supported domain.
*
* @param {generic.ViewInfo} info The view's strategy information object.
* @return {Promise<LovelaceViewConfig>}
* @param {ViewInfo} info The view's strategy information object.
*
* @remarks
* Called upon opening a subview.
*/
static async generateView(info: generic.ViewInfo): Promise<LovelaceViewConfig> {
const exposedDomainIds = Helper.getExposedDomainIds();
const area = info.view.strategy?.options?.area ?? {} as StrategyArea;
static async generateView(info: ViewInfo): Promise<LovelaceViewConfig> {
const exposedDomainNames = Registry.getExposedNames('domain');
const area = info.view.strategy?.options?.area ?? ({} as StrategyArea);
const areaEntities = new RegistryFilter(Registry.entities).whereAreaId(area.area_id).toList();
const viewCards: LovelaceCardConfig[] = [...(area.extra_cards ?? [])];
// Set the target for controller cards to the current area.
let target: HassServiceTarget = {
area_id: [area.area_id],
};
// Set the target for any Header card to the current area.
const target: HassServiceTarget = { area_id: [area.area_id] };
// Create cards for each domain.
for (const domain of exposedDomainIds) {
if (domain === "default") {
continue;
// Prepare promises for all supported domains
const domainCardPromises = exposedDomainNames.filter(isSupportedDomain).map(async (domain) => {
const moduleName = sanitizeClassName(domain + 'Card');
const entities = new RegistryFilter(areaEntities)
.whereDomain(domain)
.where((entity) => !(domain === 'switch' && entity.entity_id.endsWith('_stateful_scene')))
.toList();
if (!entities.length) {
return null;
}
const className = Helper.sanitizeClassName(domain + "Card");
let domainCards: EntityCardConfig[] = [];
const titleCard = new HeaderCard(
{ entity_id: entities.map((entity) => entity.entity_id) },
Registry.strategyOptions.domains[domain],
).createCard();
try {
domainCards = await import(`./cards/${className}`).then(cardModule => {
let domainCards: EntityCardConfig[] = [];
let entities = Helper.getDeviceEntities(area, domain);
const DomainCard = (await import(`./cards/${moduleName}`)).default;
// Exclude hidden Config and Diagnostic entities.
entities = applyEntityCategoryFilters(entities, domain);
// Set the target for controller cards to entities without an area.
if (area.area_id === "undisclosed") {
target = {
entity_id: entities.map(entity => entity.entity_id),
}
}
if (entities.length) {
// Create a Controller card for the current domain.
const titleCard = new ControllerCard(
target,
Helper.strategyOptions.domains[domain],
).createCard();
if (domain === "sensor") {
// Create a card for each sensor-entity of the current area.
const sensorStates = Helper.getStateEntities(area, "sensor");
const sensorCards: EntityCardConfig[] = [];
for (const sensor of entities) {
// Find the state of the current sensor.
const sensorState = sensorStates.find(state => state.entity_id === sensor.entity_id);
let cardOptions = Helper.strategyOptions.card_options?.[sensor.entity_id];
if (sensorState?.attributes.unit_of_measurement) {
cardOptions = {
...{
type: "custom:mini-graph-card",
entities: [sensor.entity_id],
},
...cardOptions,
};
sensorCards.push(new SensorCard(sensor, cardOptions).getCard());
}
}
if (sensorCards.length) {
domainCards.push({
type: "vertical-stack",
cards: sensorCards,
});
domainCards.unshift(titleCard);
}
return domainCards;
}
// Create a card for each other domain-entity of the current area.
for (const entity of entities) {
let deviceOptions;
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
if (entity.device_id) {
deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id];
}
domainCards.push(new cardModule[className](entity, cardOptions).getCard());
}
if (domain === "binary_sensor") {
// Horizontally group every two binary sensor cards.
const horizontalCards: EntityCardConfig[] = [];
for (let i = 0; i < domainCards.length; i += 2) {
horizontalCards.push({
type: "horizontal-stack",
cards: domainCards.slice(i, i + 2),
});
}
domainCards = horizontalCards;
}
if (domainCards.length) {
domainCards.unshift(titleCard);
}
}
return domainCards;
});
} catch (e) {
Helper.logError("An error occurred while creating the domain cards!", e);
}
if (domainCards.length) {
viewCards.push({
type: "vertical-stack",
cards: domainCards,
});
}
}
if (!Helper.strategyOptions.domains.default.hidden) {
// Create cards for any other domain.
// Collect entities of the current area and unexposed domains.
let miscellaneousEntities = Helper.getDeviceEntities(area).filter(
entity => !exposedDomainIds.includes(entity.entity_id.split(".", 1)[0])
);
// Exclude hidden Config and Diagnostic entities.
miscellaneousEntities = applyEntityCategoryFilters(miscellaneousEntities, "default");
// Create a column of miscellaneous entity cards.
if (miscellaneousEntities.length) {
let miscellaneousCards: (StackCardConfig | EntityCardConfig)[] = [];
try {
miscellaneousCards = await import("./cards/MiscellaneousCard").then(cardModule => {
const miscellaneousCards: (StackCardConfig | EntityCardConfig)[] = [
new ControllerCard(target, Helper.strategyOptions.domains.default).createCard(),
];
for (const entity of miscellaneousEntities) {
let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id];
let deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id ?? "null"];
miscellaneousCards.push(new cardModule.MiscellaneousCard(entity, cardOptions).getCard());
}
return miscellaneousCards;
});
} catch (e) {
Helper.logError("An error occurred while creating the domain cards!", e);
if (domain === 'sensor') {
const domainCards = entities
.filter((entity) => Registry.hassStates[entity.entity_id]?.attributes.unit_of_measurement)
.map((entity) => {
const options = {
...(entity.device_id && Registry.strategyOptions.card_options?.[entity.device_id]),
...Registry.strategyOptions.card_options?.[entity.entity_id],
type: 'custom:mini-graph-card',
entities: [entity.entity_id],
};
return new SensorCard(entity, options).getCard();
});
return domainCards.length ? { type: 'vertical-stack', cards: [titleCard, ...domainCards] } : null;
}
viewCards.push({
type: "vertical-stack",
cards: miscellaneousCards,
let domainCards = entities.map((entity) => {
const cardOptions = {
...(entity.device_id && Registry.strategyOptions.card_options?.[entity.device_id]),
...Registry.strategyOptions.card_options?.[entity.entity_id],
};
return new DomainCard(entity, cardOptions).getCard();
});
if (domain === 'binary_sensor') {
domainCards = stackHorizontal(domainCards);
}
return domainCards.length ? { type: 'vertical-stack', cards: [titleCard, ...domainCards] } : null;
} catch (e) {
logMessage(lvlError, `Error creating card configurations for domain ${domain}`, e);
return null;
}
});
// Await all domain card stacks
const domainCardStacks = (await Promise.all(domainCardPromises)).filter(Boolean) as LovelaceCardConfig[];
viewCards.push(...domainCardStacks);
// Miscellaneous domain
if (!Registry.strategyOptions.domains.default.hidden) {
const miscellaneousEntities = new RegistryFilter(areaEntities)
.not()
.where((entity) => isSupportedDomain(entity.entity_id.split('.', 1)[0]))
.toList();
if (miscellaneousEntities.length) {
try {
const MiscellaneousCard = (await import('./cards/MiscellaneousCard')).default;
const miscellaneousCards = [
new HeaderCard(target, Registry.strategyOptions.domains.default).createCard(),
...miscellaneousEntities.map((entity) =>
new MiscellaneousCard(entity, Registry.strategyOptions.card_options?.[entity.entity_id]).getCard(),
),
];
viewCards.push({
type: 'vertical-stack',
cards: miscellaneousCards,
});
} catch (e) {
logMessage(lvlError, 'Error creating card configurations for domain `miscellaneous`', e);
}
}
}
// Return cards.
return {
cards: viewCards,
};
return { cards: viewCards };
}
}
customElements.define("ll-strategy-mushroom-strategy", MushroomStrategy);
customElements.define('ll-strategy-mushroom-strategy', MushroomStrategy);
const version = "v2.2.0";
const version = 'v2.3.0';
console.info(
"%c Mushroom Strategy %c ".concat(version, " "),
"color: white; background: coral; font-weight: 700;", "color: coral; background: white; font-weight: 700;"
'%c Mushroom Strategy %c '.concat(version, ' '),
'color: white; background: coral; font-weight: 700;',
'color: coral; background: white; font-weight: 700;',
);

View File

@ -29,7 +29,10 @@
"off": "Aus",
"on": "Ein",
"open": "Offen",
"unclosed": "Nicht Geschlossen"
"unavailable": "Nicht verfügbar",
"unclosed": "Nicht Geschlossen",
"undisclosed": "Sonstiges",
"unknown": "Unbekannt"
},
"input_select": {
"input_selects": "Auswahl-Eingaben"
@ -39,7 +42,10 @@
"lights": "Leuchten"
},
"lock": {
"locks": "Schlösser"
"locked": "Gesperrt",
"all_locks": "Alle Schlösser",
"locks": "Schlösser",
"unlocked": "Entsperrt"
},
"media_player": {
"media_players": "Wiedergabegeräte"
@ -61,5 +67,14 @@
"vacuum": {
"all_vacuums": "Alle Staubsauger",
"vacuums": "Staubsauger"
},
"valve": {
"all_valves": "Alle Ventile",
"valves": "Ventile",
"open": "Offen",
"opening": "Öffnet",
"closed": "Geschlossen",
"closing": "Schließt",
"stopped": "Gestoppt"
}
}

View File

@ -29,7 +29,10 @@
"off": "Off",
"on": "On",
"open": "Open",
"unclosed": "Unclosed"
"unavailable": "Unavailable",
"unclosed": "Unclosed",
"undisclosed": "Other",
"unknown": "Unknown"
},
"input_select": {
"input_selects": "Input Selects"
@ -39,10 +42,13 @@
"lights": "Lights"
},
"lock": {
"locks": "Locks"
"all_locks": "All Locks",
"locked": "Locked",
"locks": "Locks",
"unlocked": "Unlocked"
},
"media_player": {
"media_players": "Mediaplayers"
"media_players": "Media Players"
},
"scene": {
"scenes": "Scenes"
@ -61,5 +67,14 @@
"vacuum": {
"all_vacuums": "All Vacuums",
"vacuums": "Vacuums"
},
"valve": {
"all_valves": "All Valves",
"valves": "Valves",
"open": "Open",
"opening": "Opening",
"closed": "Closed",
"closing": "Closing",
"stopped": "Stopped"
}
}

View File

@ -29,7 +29,10 @@
"off": "Apagado",
"on": "Encendido",
"open": "Abierto",
"unclosed": "Sin Cerrar"
"unavailable": "No Disponible",
"unclosed": "Sin Cerrar",
"undisclosed": "Varios",
"unknown": "Desconocido"
},
"input_select": {
"input_selects": "Selecciones de Entrada"
@ -39,11 +42,17 @@
"lights": "Luces"
},
"lock": {
"locks": "Candados"
"all_locks": "Todas las Candados",
"locked": "Locked",
"locks": "Candados",
"unlocked": "Desbloqueado"
},
"media_player": {
"media_players": "Reproductores Multimedia"
},
"scene": {
"scenes": "Scenas"
},
"select": {
"selects": "Seleccionar"
},
@ -58,5 +67,14 @@
"vacuum": {
"all_vacuums": "Todas las Aspiradoras",
"vacuums": "Aspiradoras"
},
"valve": {
"all_valves": "Todas las válvulas",
"valves": "Válvulas",
"open": "Abierta",
"opening": "Abriendo",
"closed": "Cerrada",
"closing": "Cerrando",
"stopped": "Detenida"
}
}

View File

@ -19,9 +19,9 @@
"all": "Alle",
"areas": "Ruimtes",
"busy": "Bezig",
"good_afternoon": "Goede middag",
"good_evening": "Goede avond",
"good_morning": "Goede morgen",
"good_afternoon": "Goedemiddag",
"good_evening": "Goedeavond",
"good_morning": "Goedemorgen",
"hello": "Hallo",
"home": "Start",
"miscellaneous": "Overige",
@ -29,7 +29,10 @@
"off": "Uit",
"on": "Aan",
"open": "Open",
"unclosed": "Ongesloten"
"unavailable": "Onbeschikbaar",
"unclosed": "Niet Gesloten",
"undisclosed": "Overige",
"unknown": "Onbekend"
},
"input_select": {
"input_selects": "Lijsten"
@ -39,7 +42,10 @@
"lights": "Lampen"
},
"lock": {
"locks": "Sloten"
"all_locks": "Alle Sloten",
"locked": "Vergrendeld",
"locks": "Sloten",
"unlocked": "Ontgrendeld"
},
"media_player": {
"media_players": "Mediaspelers"
@ -61,5 +67,14 @@
"vacuum": {
"all_vacuums": "Alle Afzuiging",
"vacuums": "Afzuiging"
},
"valve": {
"all_valves": "Alle kleppen",
"valves": "Kleppen",
"open": "Open",
"opening": "Openen",
"closed": "Gesloten",
"closing": "Sluiten",
"stopped": "Gestopt"
}
}

View File

@ -7,7 +7,7 @@ This means properties are added/removed from the originals and subtypes may have
The [Apache 2.0 License](https://github.com/home-assistant/frontend/blob/dev/LICENSE.md) applies to all files in this
directory.
Copyright 2023 Ferry Cools
Copyright 2023 Ferry Cools
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -0,0 +1,46 @@
import type { TranslationDict } from '../../types';
// Exclude some patterns from key type checking for now
// These are intended to be removed as errors are fixed
// Fixing component category will require tighter definition of types from backend and/or web socket
export type LocalizeKeys =
| FlattenObjectKeys<Omit<TranslationDict, 'supervisor'>>
| `panel.${string}`
| `ui.card.alarm_control_panel.${string}`
| `ui.card.weather.attributes.${string}`
| `ui.card.weather.cardinal_direction.${string}`
| `ui.card.lawn_mower.actions.${string}`
| `ui.components.calendar.event.rrule.${string}`
| `ui.components.selectors.file.${string}`
| `ui.components.logbook.messages.detected_device_classes.${string}`
| `ui.components.logbook.messages.cleared_device_classes.${string}`
| `ui.dialogs.entity_registry.editor.${string}`
| `ui.dialogs.more_info_control.lawn_mower.${string}`
| `ui.dialogs.more_info_control.vacuum.${string}`
| `ui.dialogs.quick-bar.commands.${string}`
| `ui.dialogs.unhealthy.reason.${string}`
| `ui.dialogs.unsupported.reason.${string}`
| `ui.panel.config.${string}.${'caption' | 'description'}`
| `ui.panel.config.dashboard.${string}`
| `ui.panel.config.zha.${string}`
| `ui.panel.config.zwave_js.${string}`
| `ui.panel.lovelace.card.${string}`
| `ui.panel.lovelace.editor.${string}`
| `ui.panel.page-authorize.form.${string}`
| `component.${string}`;
// Tweaked from https://www.raygesualdo.com/posts/flattening-object-keys-with-typescript-types
export type FlattenObjectKeys<T extends Record<string, any>, Key extends keyof T = keyof T> = Key extends string
? T[Key] extends Record<string, unknown>
? `${Key}.${FlattenObjectKeys<T[Key]>}`
: `${Key}`
: never;
// Later, don't return string when HTML is passed, and don't allow undefined
export type LocalizeFunc<Keys extends string = LocalizeKeys> = (
key: Keys,
values?: Record<
string,
string | number | { _$litType$: 1; strings: TemplateStringsArray; values: Array<unknown> } | null | undefined
>,
) => string;

View File

@ -1,20 +1,29 @@
import { RegistryEntry } from './registry';
/**
* Area Entity.
* Represents an entry in the Area Registry in Home Assistant.
*
* @property {string} area_id The id of the area.
* @property {string|null} floor_id The id of the area's floor.
* @property {string} name Name of the area.
* @property {string|null} picture URL to a picture that should be used instead of showing the domain icon.
* @property {string|null} icon Icon to show.
* @property {string[]} labels Labels allow grouping elements irrespective of their physical location or type.
* @property {string[]} aliases Array of aliases of the area.
* @property {string[]} aliases - An array of aliases for the area.
* @property {string} area_id - The unique identifier for the area.
* @property {string|null} floor_id - The identifier for the area's floor, or null if not applicable.
* @property {string|null} humidity_entity_id - The identifier for the area's humidity sensor, or null if not
* applicable.
* @property {string|null} icon - The icon to display for the area, or null if not specified.
* @property {string[]} labels - Labels for grouping elements irrespective of their physical location or type.
* @property {string} name - The name of the area.
* @property {string|null} picture - The URL to a picture that should be used instead of the domain icon, or null if
* not specified.
* @property {string|null} temperature_entity_id - The identifier for the area's temperature sensor, or null if not
* applicable.
*/
export interface AreaRegistryEntry {
export interface AreaRegistryEntry extends RegistryEntry {
aliases: string[];
area_id: string;
floor_id: string | null;
name: string;
picture: string | null;
humidity_entity_id: string | null;
icon: string | null;
labels: string[];
aliases: string[];
name: string;
picture: string | null;
temperature_entity_id: string | null;
}

View File

@ -1,19 +1,5 @@
export const HVAC_MODES = [
"auto",
"heat_cool",
"heat",
"cool",
"dry",
"fan_only",
"off",
] as const;
/** Represents the available HVAC modes for climate control in Home Assistant. */
export const HVAC_MODES = ['auto', 'heat_cool', 'heat', 'cool', 'dry', 'fan_only', 'off'] as const;
/** Represents a type for HVAC modes in Home Assistant. */
export type HvacMode = (typeof HVAC_MODES)[number];
HVAC_MODES.reduce(
(order, mode, index) => {
order[mode] = index;
return order;
},
{} as Record<HvacMode, number>
);

View File

@ -1,38 +1,46 @@
/**
* Device Entity.
* Represents a device entity in the of Home Assistant's device registry .
*
* @property {string} id Unique ID of a device (generated by Home Assistant).
* @property {string[]} config_entries
* @property {Array} connections
* @property {Array} identifiers
* @property {string | null} manufacturer
* @property {string | null} model
* @property {string | null} name
* @property {string | null} sw_version
* @property {string | null} hw_version
* @property {string | null} serial_number
* @property {string | null} via_device_id
* @property {string} area_id The Area which the device is placed in.
* @property {string | null} name_by_user
* @property {string[] | null} entry_type
* @property {string | null} disabled_by Indicates by what this entity is disabled.
* @property {string | null} configuration_url
* @property {string} id - Unique identifier of the device (generated by Home Assistant).
* @property {string[]} config_entries - Config entries linked to this device.
* @property {Record<string, (string | null)[]>} config_entries_subentries - Subentries for the config entries.
* @property {[string, string][]} connections - Tuples of (connection_type, connection identifier).
* @property {[string, string][]} identifiers - Set of (DOMAIN, identifier) tuples identifying the device.
* @property {string | null} manufacturer - The manufacturer of the device.
* @property {string | null} model - The model name of the device.
* @property {string | null} model_id - The model identifier of the device.
* @property {string | null} name - The name of the device.
* @property {string[]} labels - Labels for the device.
* @property {string | null} sw_version - The firmware version of the device.
* @property {string | null} hw_version - The hardware version of the device.
* @property {string | null} serial_number - The serial number of the device.
* @property {string | null} via_device_id - Identifier of a device that routes messages to this device.
* @property {string | null} area_id - The area which the device is placed in.
* @property {string | null} name_by_user - User configured name of the device.
* @property {string[] | null} entry_type - The type of entry (e.g., service).
* @property {string | null} disabled_by - Indicates what disabled this entity.
* @property {string | null} configuration_url - URL for configuring the device.
* @property {string | null} primary_config_entry - Identifier of the primary config entry for the device.
*/
export interface DeviceRegistryEntry {
id: string;
config_entries: string[];
connections: Array<[string, string]>;
identifiers: Array<[string, string]>;
config_entries_subentries: Record<string, (string | null)[]>;
connections: [string, string][];
identifiers: [string, string][];
manufacturer: string | null;
model: string | null;
model_id: string | null;
name: string | null;
labels: string[];
sw_version: string | null;
hw_version: string | null;
serial_number: string | null;
via_device_id: string | null;
area_id: string | null;
name_by_user: string | null;
entry_type: "service" | null;
disabled_by: "user" | "integration" | "config_entry" | null;
entry_type: 'service' | null;
disabled_by: 'user' | 'integration' | 'config_entry' | null;
configuration_url: string | null;
primary_config_entry: string | null;
}

View File

@ -1,76 +1,143 @@
import {LightColor} from "./light";
import { LightColor } from './light';
type EntityCategory = "config" | "diagnostic";
export type EntityCategory = 'config' | 'diagnostic';
/**
* Represents the display entry for an entity in the entity registry.
*
* @property {string} entity_id - The unique identifier for the entity.
* @property {string} [name] - The name of the entity.
* @property {string} [icon] - The icon associated with the entity.
* @property {string} [device_id] - The ID of the device linked to this entity.
* @property {string} [area_id] - The ID of the area linked to this entity.
* @property {string[]} labels - Labels associated with the entity.
* @property {boolean} [hidden] - Indicates if the entity is hidden.
* @property {EntityCategory} [entity_category] - The category of the entity.
* @property {string} [translation_key] - The translation key for the entity.
* @property {string} [platform] - The platform of the entity.
* @property {number} [display_precision] - The display precision for the entity.
* @property {boolean} [has_entity_name] - Indicates if the entity has a name.
*/
export interface EntityRegistryDisplayEntry {
entity_id: string;
name?: string;
icon?: string;
device_id?: string;
area_id?: string;
labels: string[];
hidden?: boolean;
entity_category?: EntityCategory;
translation_key?: string;
platform?: string;
display_precision?: number;
has_entity_name?: boolean;
}
/**
* Home assistant entity.
* Represents an entity in the entity registry of Home Assistant.
*
* @property {string} id
* @property {string} entity_id The id of this entity.
* @property {string} name The name of this entity.
* @property {string | null} icon
* @property {string | null} platform
* @property {string | null} config_entry_id
* @property {string | null} device_id The id of the device to which this entity is linked.
* @property {string | null} area_id The id of the area to which this entity is linked.
* @property {string | null} disabled_by Indicates by what this entity is disabled.
* @property {Object} hidden_by Indicates by what this entity is hidden.
* @property {EntityCategory | null} entity_category
* @property {boolean} has_entity_name
* @property {string} [original_name]
* @property {string} unique_id
* @property {string} [translation_key]
* @property {EntityRegistryOptions | null} options
* @property {string} id - The unique identifier for the entity.
* @property {string} entity_id - The ID of the entity.
* @property {string | null} name - The name of the entity.
* @property {string | null} icon - The icon associated with the entity.
* @property {string | null} platform - The platform of the entity.
* @property {string | null} config_entry_id - The ID of the config entry associated with the entity.
* @property {string | null} config_subentry_id - The ID of the config subentry associated with the entity.
* @property {string | null} device_id - The ID of the device linked to this entity.
* @property {string | null} area_id - The ID of the area linked to this entity.
* @property {string[]} labels - Labels associated with the entity.
* @property {"user" | "device" | "integration" | "config_entry" | null} disabled_by - Indicates what disabled this
* entity.
* @property {Exclude<EntityRegistryEntry["disabled_by"], "config_entry">} hidden_by - Indicates what hidden this
* entity.
* @property {EntityCategory | null} entity_category - The category of the entity.
* @property {boolean} has_entity_name - Indicates if the entity has a name.
* @property {string} [original_name] - The original name of the entity.
* @property {string} unique_id - The unique identifier for the entity.
* @property {string} [translation_key] - The translation key for the entity.
* @property {EntityRegistryOptions | null} options - Additional options for the entity.
* @property {Record<string, string>} categories - Categories associated with the entity.
*/
export interface EntityRegistryEntry {
id: string;
entity_id: string;
name: string | null;
icon: string | null;
platform: string;
platform: string | null;
config_entry_id: string | null;
config_subentry_id: string | null;
device_id: string | null;
area_id: string | null;
disabled_by: "user" | "device" | "integration" | "config_entry" | null;
hidden_by: Exclude<EntityRegistryEntry["disabled_by"], "config_entry">;
labels: string[];
disabled_by: 'user' | 'device' | 'integration' | 'config_entry' | null;
hidden_by: Exclude<EntityRegistryEntry['disabled_by'], 'config_entry'>;
entity_category: EntityCategory | null;
has_entity_name: boolean;
original_name?: string;
unique_id: string;
translation_key?: string;
options: EntityRegistryOptions | null;
categories: Record<string, string>;
}
/**
* Represents options for a sensor entity in Home Assistant.
*
* @property {number | null} [display_precision] - The display precision for the sensor.
* @property {number | null} [suggested_display_precision] - Suggested display precision for the sensor.
* @property {string | null} [unit_of_measurement] - The unit of measurement for the sensor.
*/
export interface SensorEntityOptions {
display_precision?: number | null;
suggested_display_precision?: number | null;
unit_of_measurement?: string | null;
}
/**
* Represents options for a light entity in Home Assistant.
*
* @property {LightColor[]} [favorite_colors] - An array of favorite colors for the light.
*/
export interface LightEntityOptions {
favorite_colors?: LightColor[];
}
/**
* Represents options for a number entity in Home Assistant.
*
* @property {string | null} [unit_of_measurement] - The unit of measurement for the number.
*/
export interface NumberEntityOptions {
unit_of_measurement?: string | null;
}
/**
* Represents options for a lock entity in Home Assistant.
*
* @property {string | null} [default_code] - The default code for the lock.
*/
export interface LockEntityOptions {
default_code?: string | null;
}
/**
* Represents options for an alarm control panel entity in Home Assistant.
*
* @property {string | null} [default_code] - The default code for the alarm control panel.
*/
export interface AlarmControlPanelEntityOptions {
default_code?: string | null;
}
/**
* Represents options for a weather entity in Home Assistant.
*
* @property {string | null} [precipitation_unit] - The unit of measurement for precipitation.
* @property {string | null} [pressure_unit] - The unit of measurement for pressure.
* @property {string | null} [temperature_unit] - The unit of measurement for temperature.
* @property {string | null} [visibility_unit] - The unit of measurement for visibility.
* @property {string | null} [wind_speed_unit] - The unit of measurement for wind speed.
*/
export interface WeatherEntityOptions {
precipitation_unit?: string | null;
pressure_unit?: string | null;
@ -79,19 +146,40 @@ export interface WeatherEntityOptions {
wind_speed_unit?: string | null;
}
/**
* Represents options for a switch entity in Home Assistant.
*
* @property {string} entity_id - The ID of the entity.
* @property {boolean} invert - Indicates if the switch should be inverted.
*/
export interface SwitchAsXEntityOptions {
entity_id: string;
invert: boolean;
}
/**
* Represents options for an entity in the entity registry of Home Assistant.
*
* @property {NumberEntityOptions} [number] - Options for number entities.
* @property {SensorEntityOptions} [sensor] - Options for sensor entities.
* @property {AlarmControlPanelEntityOptions} [alarm_control_panel] - Options for alarm control panel entities.
* @property {LockEntityOptions} [lock] - Options for lock entities.
* @property {WeatherEntityOptions} [weather] - Options for weather entities.
* @property {LightEntityOptions} [light] - Options for light entities.
* @property {SwitchAsXEntityOptions} [switch_as_x] - Options for switch entities.
* @property {Record<string, unknown>} [conversation] - Options for conversation entities.
* @property {Record<string, unknown>} ["cloud.alexa"] - Options for Alexa cloud integration.
* @property {Record<string, unknown>} ["cloud.google_assistant"] - Options for Google Assistant cloud integration.
*/
export interface EntityRegistryOptions {
number?: NumberEntityOptions;
sensor?: SensorEntityOptions;
alarm_control_panel?: AlarmControlPanelEntityOptions;
lock?: LockEntityOptions;
weather?: WeatherEntityOptions;
light?: LightEntityOptions;
switch_as_x?: SwitchAsXEntityOptions;
conversation?: Record<string, unknown>;
"cloud.alexa"?: Record<string, unknown>;
"cloud.google_assistant"?: Record<string, unknown>;
'cloud.alexa'?: Record<string, unknown>;
'cloud.google_assistant'?: Record<string, unknown>;
}

View File

@ -0,0 +1,18 @@
import { RegistryEntry } from './registry';
/**
* Represents a floor entry in the Floor Registry of Home Assistant.
*
* @property {string} floor_id - The unique identifier for the floor.
* @property {string} name - The name of the floor.
* @property {number | null} level - The level of the floor (optional).
* @property {string | null} icon - The icon associated with the floor (optional).
* @property {string[]} aliases - An array of aliases for the floor.
*/
export interface FloorRegistryEntry extends RegistryEntry {
floor_id: string;
name: string;
level: number | null;
icon: string | null;
aliases: string[];
}

View File

@ -0,0 +1,8 @@
/**
* Represents user data for the frontend in Home Assistant.
*
* @property {boolean} [showAdvanced] - Indicates whether advanced options should be shown to the user.
*/
export interface CoreFrontendUserData {
showAdvanced?: boolean;
}

View File

@ -1,6 +1,16 @@
/**
* Represents the color options for a light entity in Home Assistant.
*
* @property {number} [color_temp_kelvin] - The color temperature in Kelvin.
* @property {[number, number]} [hs_color] - The hue and saturation values.
* @property {[number, number, number]} [rgb_color] - The red, green, and blue color values.
* @property {[number, number, number, number]} [rgbw_color] - The red, green, blue, and white color values.
* @property {[number, number, number, number, number]} [rgbww_color] - The red, green, blue, white, and warm white
* color values.
*/
export type LightColor =
| { color_temp_kelvin: number; }
| { hs_color: [number, number]; }
| { rgb_color: [number, number, number]; }
| { rgbw_color: [number, number, number, number]; }
| { rgbww_color: [number, number, number, number, number]; };
| { color_temp_kelvin: number }
| { hs_color: [number, number] }
| { rgb_color: [number, number, number] }
| { rgbw_color: [number, number, number, number] }
| { rgbww_color: [number, number, number, number, number] };

View File

@ -1,118 +0,0 @@
import {HassServiceTarget} from "home-assistant-js-websocket";
export type LovelaceStrategyConfig = {
type: string;
[key: string]: any;
};
export interface LovelaceConfig {
title?: string;
strategy?: LovelaceStrategyConfig;
views: LovelaceViewConfig[];
background?: string;
}
/**
* View Config.
*
* @see https://www.home-assistant.io/dashboards/views/
*/
export interface LovelaceViewConfig {
index?: number;
title?: string;
type?: string;
strategy?: LovelaceStrategyConfig;
badges?: Array<string | LovelaceBadgeConfig>;
cards?: LovelaceCardConfig[];
path?: string;
icon?: string;
theme?: string;
panel?: boolean;
background?: string;
visible?: boolean | ShowViewConfig[];
subview?: boolean;
back_path?: string;
}
export interface ShowViewConfig {
user?: string;
}
export interface LovelaceBadgeConfig {
type?: string;
[key: string]: any;
}
export interface LovelaceCardConfig {
index?: number;
view_index?: number;
view_layout?: any;
type: string;
[key: string]: any;
}
export interface ToggleActionConfig extends BaseActionConfig {
action: "toggle";
}
export interface CallServiceActionConfig extends BaseActionConfig {
action: "call-service";
service: string;
target?: HassServiceTarget;
// Property "service_data" is kept for backwards compatibility. Replaced by "data".
service_data?: Record<string, unknown>;
data?: Record<string, unknown>;
}
export interface NavigateActionConfig extends BaseActionConfig {
action: "navigate";
navigation_path: string;
navigation_replace?: boolean;
}
export interface UrlActionConfig extends BaseActionConfig {
action: "url";
url_path: string;
}
export interface MoreInfoActionConfig extends BaseActionConfig {
action: "more-info";
}
export interface AssistActionConfig extends BaseActionConfig {
action: "assist";
pipeline_id?: string;
start_listening?: boolean;
}
export interface NoActionConfig extends BaseActionConfig {
action: "none";
}
export interface CustomActionConfig extends BaseActionConfig {
action: "fire-dom-event";
}
export interface BaseActionConfig {
action: string;
confirmation?: ConfirmationRestrictionConfig;
}
export interface ConfirmationRestrictionConfig {
text?: string;
exemptions?: RestrictionConfig[];
}
export interface RestrictionConfig {
user: string;
}
export type ActionConfig =
| ToggleActionConfig
| CallServiceActionConfig
| NavigateActionConfig
| UrlActionConfig
| MoreInfoActionConfig
| AssistActionConfig
| NoActionConfig
| CustomActionConfig;

View File

@ -0,0 +1,145 @@
import { HassServiceTarget } from 'home-assistant-js-websocket';
/**
* Represents the configuration for a toggle action in Home Assistant.
*
* @property {'toggle'} action - The action type, which is "toggle".
*/
export interface ToggleActionConfig extends BaseActionConfig {
action: 'toggle';
}
/**
* Represents the configuration for a call service action in Home Assistant.
*
* @property {'call-service' | 'perform-action'} action - The action type, which can be "call-service" or
* "perform-action".
* @property {string} [service] - Deprecated; service name for backward compatibility.
* @property {string} perform_action - The action to perform.
* @property {HassServiceTarget} [target] - The target for the service call.
* @property {Record<string, unknown>} [service_data] - Deprecated; service data for backward compatibility.
* @property {Record<string, unknown>} [data] - The data to send with the service call.
*/
export interface CallServiceActionConfig extends BaseActionConfig {
action: 'call-service' | 'perform-action';
/** @deprecated "service" is kept for backwards compatibility. Replaced by "perform_action". */
service?: string;
perform_action: string;
target?: HassServiceTarget;
/** @deprecated "service_data" is kept for backwards compatibility. Replaced by "data". */
service_data?: Record<string, unknown>;
data?: Record<string, unknown>;
}
/**
* Represents the configuration for a navigate action in Home Assistant.
*
* @property {'navigate'} action - The action type, which is "navigate".
* @property {string} navigation_path - The path to navigate to.
* @property {boolean} [navigation_replace] - Whether to replace the current history entry.
*/
export interface NavigateActionConfig extends BaseActionConfig {
action: 'navigate';
navigation_path: string;
navigation_replace?: boolean;
}
/**
* Represents the configuration for a URL action in Home Assistant.
*
* @property {'url'} action - The action type, which is "url".
* @property {string} url_path - The URL path to navigate to.
*/
export interface UrlActionConfig extends BaseActionConfig {
action: 'url';
url_path: string;
}
/**
* Represents the configuration for a more info action in Home Assistant.
*
* @property {'more-info'} action - The action type, which is "more-info".
* @property {string} [entity] - The entity to show more information about.
*/
export interface MoreInfoActionConfig extends BaseActionConfig {
action: 'more-info';
entity?: string;
}
/**
* Represents the configuration for an assist action in Home Assistant.
*
* @property {'assist'} action - The action type, which is "assist".
* @property {string} [pipeline_id] - The ID of the pipeline to use for the assist action.
* @property {boolean} [start_listening] - Whether to start listening for user input.
*/
export interface AssistActionConfig extends BaseActionConfig {
action: 'assist';
pipeline_id?: string;
start_listening?: boolean;
}
/**
* Represents the configuration for a no action in Home Assistant.
*
* @property {'none'} action - The action type, which is "none".
*/
export interface NoActionConfig extends BaseActionConfig {
action: 'none';
}
/**
* Represents the configuration for a custom action in Home Assistant.
*
* @property {'fire-dom-event'} action - The action type, which is "fire-dom-event".
*/
export interface CustomActionConfig extends BaseActionConfig {
action: 'fire-dom-event';
}
/**
* Represents the base configuration for an action in Home Assistant.
*
* @property {string} action - The type of action to perform.
* @property {ConfirmationRestrictionConfig} [confirmation] - Optional confirmation settings for the action.
*/
export interface BaseActionConfig {
action: string;
confirmation?: ConfirmationRestrictionConfig;
}
/**
* Represents the confirmation restriction configuration for an action in Home Assistant.
*
* @property {string} [text] - The confirmation text.
* @property {RestrictionConfig[]} [exemptions] - List of exemptions for the confirmation.
*/
export interface ConfirmationRestrictionConfig {
text?: string;
exemptions?: RestrictionConfig[];
}
/**
* Represents a restriction configuration in Home Assistant.
*
* @property {string} user - The user associated with the restriction.
*/
export interface RestrictionConfig {
user: string;
}
/**
* Represents the overall action configuration in Home Assistant.
*
* A union type representing different action-configurations available in Home Assistant.
* Each action type can have its own specific configuration.
*/
export type ActionConfig =
| ToggleActionConfig
| CallServiceActionConfig
| NavigateActionConfig
| UrlActionConfig
| MoreInfoActionConfig
| AssistActionConfig
| NoActionConfig
| CustomActionConfig;

View File

@ -0,0 +1,15 @@
import { Condition } from '../../../panels/common/validate-condition';
/**
* Represents the configuration for a Lovelace badge in Home Assistant.
*
* @property {string} type - The type of the badge.
* @property {Condition[]} [visibility] - An optional array of visibility conditions for the badge.
* @property {any} [key] - Additional properties can be included in the configuration.
*/
export interface LovelaceBadgeConfig {
type: string;
visibility?: Condition[];
[key: string]: any;
}

View File

@ -0,0 +1,27 @@
import { Condition } from '../../../panels/common/validate-condition';
import { LovelaceGridOptions, LovelaceLayoutOptions } from '../../../panels/lovelace/types';
/**
* Represents the configuration for a Lovelace card in Home Assistant.
*
* @property {number} [index] - The index of the card in the view.
* @property {number} [view_index] - The index of the view the card belongs to.
* @property {any} [view_layout] - The layout options for the card view.
* @property {LovelaceLayoutOptions} [layout_options] - Deprecated layout options; use `grid_options` instead.
* @property {LovelaceGridOptions} [grid_options] - The grid options for the card layout.
* @property {string} type - The type of the card.
* @property {Condition[]} [visibility] - An optional array of visibility conditions for the card.
* @property {any} [key] - Additional properties can be included in the configuration.
*/
export interface LovelaceCardConfig {
index?: number;
view_index?: number;
view_layout?: any;
/** @deprecated Use `grid_options` instead */
layout_options?: LovelaceLayoutOptions;
grid_options?: LovelaceGridOptions;
type: string;
visibility?: Condition[];
[key: string]: any;
}

View File

@ -0,0 +1,42 @@
import { Condition } from '../../../panels/common/validate-condition';
import { LovelaceCardConfig } from './card';
import { LovelaceStrategyConfig } from './strategy';
/**
* Represents the base configuration for a Lovelace section in Home Assistant.
*
* @property {Condition[]} [visibility] - An optional array of visibility conditions for the section.
* @property {number} [column_span] - The number of columns the section spans.
* @property {number} [row_span] - The number of rows the section spans.
* @property {string} [title] - The title of the section (deprecated; use heading card instead).
*/
export interface LovelaceBaseSectionConfig {
visibility?: Condition[];
column_span?: number;
row_span?: number;
/** @deprecated Use heading card instead. */
title?: string;
}
export interface LovelaceSectionConfig /**
* Represents the configuration for a Lovelace section in Home Assistant.
*
* @property {string} [type] - The type of the section.
* @property {LovelaceCardConfig[]} [cards] - An optional array of cards contained within the section.
*/
extends LovelaceBaseSectionConfig {
type?: string;
cards?: LovelaceCardConfig[];
}
/**
* Represents the configuration for a Lovelace strategy section in Home Assistant.
*
* @property {LovelaceStrategyConfig} strategy - The strategy configuration for the section.
*/
export interface LovelaceStrategySectionConfig extends LovelaceBaseSectionConfig {
strategy: LovelaceStrategyConfig;
}
/** Represents the raw configuration for a Lovelace section in Home Assistant. */
export type LovelaceSectionRawConfig = LovelaceSectionConfig | LovelaceStrategySectionConfig;

View File

@ -0,0 +1,11 @@
/**
* Represents the configuration for a Lovelace strategy in Home Assistant.
*
* @property {string} type - The type of the strategy.
* @property {any} [key] - Additional properties can be included in the configuration.
*/
export interface LovelaceStrategyConfig {
type: string;
[key: string]: any;
}

View File

@ -0,0 +1,15 @@
import { LovelaceViewRawConfig } from './view';
/** Represents the base configuration for a Lovelace dashboard in Home Assistant. */
export interface LovelaceDashboardBaseConfig {}
/**
* Represents the configuration for a Lovelace dashboard in Home Assistant.
*
* @property {string} [background] - An optional background image or color for the dashboard.
* @property {LovelaceViewRawConfig[]} views - An array of views contained within the dashboard.
*/
export interface LovelaceConfig extends LovelaceDashboardBaseConfig {
background?: string;
views: LovelaceViewRawConfig[];
}

View File

@ -0,0 +1,117 @@
import { LovelaceBadgeConfig } from './badge';
import { LovelaceCardConfig } from './card';
import { LovelaceSectionRawConfig } from './section';
import { LovelaceStrategyConfig } from './strategy';
/**
* Represents the configuration for showing a view in Home Assistant.
*
* @property {string} [user] - The user associated with the view.
*/
export interface ShowViewConfig {
user?: string;
}
/**
* Represents the background configuration for a Lovelace view in Home Assistant.
*
* @property {string} [image] - The background image URL.
* @property {number} [opacity] - The opacity of the background.
* @property {'auto' | 'cover' | 'contain'} [size] - The size of the background image.
* @property {'top left' | 'top center' | 'top right' | 'center left' | 'center' | 'center right' | 'bottom left' |
* 'bottom center' | 'bottom right'} [alignment] - The alignment of the background image.
* @property {'repeat' | 'no-repeat'} [repeat] - The repeat behavior of the background image.
* @property {'scroll' | 'fixed'} [attachment] - The attachment behavior of the background image.
*/
export interface LovelaceViewBackgroundConfig {
image?: string;
opacity?: number;
size?: 'auto' | 'cover' | 'contain';
alignment?:
| 'top left'
| 'top center'
| 'top right'
| 'center left'
| 'center'
| 'center right'
| 'bottom left'
| 'bottom center'
| 'bottom right';
repeat?: 'repeat' | 'no-repeat';
attachment?: 'scroll' | 'fixed';
}
/**
* Represents the header configuration for a Lovelace view in Home Assistant.
*
* @property {LovelaceCardConfig} [card] - The card to be displayed in the header.
* @property {'start' | 'center' | 'responsive'} [layout] - The layout of the header.
* @property {'bottom' | 'top'} [badges_position] - The position of badges in the header.
*/
export interface LovelaceViewHeaderConfig {
card?: LovelaceCardConfig;
layout?: 'start' | 'center' | 'responsive';
badges_position?: 'bottom' | 'top';
}
/**
* Represents the base configuration for a Lovelace view in Home Assistant.
*
* @property {number} [index] - The index of the view.
* @property {string} [title] - The title of the view.
* @property {string} [path] - The path to the view.
* @property {string} [icon] - The icon for the view.
* @property {string} [theme] - The theme for the view.
* @property {boolean} [panel] - Whether the view is a panel view.
* @property {string | LovelaceViewBackgroundConfig} [background] - The background configuration for the view.
* @property {boolean | ShowViewConfig[]} [visible] - Visibility settings for the view.
* @property {boolean} [subview] - Whether the view is a subview.
* @property {string} [back_path] - The path to go back to the previous view.
* @property {number} [max_columns] - The maximum number of columns in the view.
* @property {boolean} [dense_section_placement] - Whether to place sections densely.
* @property {boolean} [top_margin] - Whether to add top margin to the view.
*/
export interface LovelaceBaseViewConfig {
index?: number;
title?: string;
path?: string;
icon?: string;
theme?: string;
panel?: boolean;
background?: string | LovelaceViewBackgroundConfig;
visible?: boolean | ShowViewConfig[];
subview?: boolean;
back_path?: string;
max_columns?: number;
dense_section_placement?: boolean;
top_margin?: boolean;
}
/**
* Represents the configuration for a Lovelace view in Home Assistant.
*
* @property {string} [type] - The type of the view.
* @property {(string | Partial<LovelaceBadgeConfig>)[]} [badges] - An array of badges for the view.
* @property {LovelaceCardConfig[]} [cards] - An array of cards in the view.
* @property {LovelaceSectionRawConfig[]} [sections] - An array of sections in the view.
* @property {LovelaceViewHeaderConfig} [header] - The header configuration for the view.
*/
export interface LovelaceViewConfig extends LovelaceBaseViewConfig {
type?: string;
badges?: (string | Partial<LovelaceBadgeConfig>)[];
cards?: LovelaceCardConfig[];
sections?: LovelaceSectionRawConfig[];
header?: LovelaceViewHeaderConfig;
}
/**
* Represents the configuration for a Lovelace strategy view in Home Assistant.
*
* @property {LovelaceStrategyConfig} strategy - The strategy configuration for the view.
*/
export interface LovelaceStrategyViewConfig extends LovelaceBaseViewConfig {
strategy: LovelaceStrategyConfig;
}
/**Represents the raw configuration for a Lovelace view in Home Assistant. */
export type LovelaceViewRawConfig = LovelaceViewConfig | LovelaceStrategyViewConfig;

View File

@ -0,0 +1,10 @@
/**
* Represents a registry entry in Home Assistant.
*
* @property {number} created_at - The timestamp when the entry was created.
* @property {number} modified_at - The timestamp when the entry was last modified.
*/
export interface RegistryEntry {
created_at: number;
modified_at: number;
}

View File

@ -0,0 +1,116 @@
// noinspection JSUnusedGlobalSymbols
import { HomeAssistant } from '../types';
/** Represents the different formats for numbers in Home Assistant. */
export enum NumberFormat {
language = 'language',
system = 'system',
comma_decimal = 'comma_decimal',
decimal_comma = 'decimal_comma',
space_comma = 'space_comma',
none = 'none',
}
/**Represents the different formats for time in Home Assistant. */
export enum TimeFormat {
language = 'language',
system = 'system',
am_pm = '12',
twenty_four = '24',
}
/** Represents the different time zones in Home Assistant. */
export enum TimeZone {
local = 'local',
server = 'server',
}
/** Represents the different formats for dates in Home Assistant. */
export enum DateFormat {
language = 'language',
system = 'system',
DMY = 'DMY',
MDY = 'MDY',
YMD = 'YMD',
}
/**Represents the first weekday in Home Assistant. */
export enum FirstWeekday {
language = 'language',
monday = 'monday',
tuesday = 'tuesday',
wednesday = 'wednesday',
thursday = 'thursday',
friday = 'friday',
saturday = 'saturday',
sunday = 'sunday',
}
/**
* Represents the locale data for the frontend in Home Assistant.
*
* @property {string} language - The language of the frontend.
* @property {NumberFormat} number_format - The format for numbers.
* @property {TimeFormat} time_format - The format for time.
* @property {DateFormat} date_format - The format for dates.
* @property {FirstWeekday} first_weekday - The first weekday.
* @property {TimeZone} time_zone - The time zone.
*/
export interface FrontendLocaleData {
language: string;
number_format: NumberFormat;
time_format: TimeFormat;
date_format: DateFormat;
first_weekday: FirstWeekday;
time_zone: TimeZone;
}
/** Represents a category for translations in Home Assistant. */
export type TranslationCategory =
| 'title'
| 'state'
| 'entity'
| 'entity_component'
| 'exceptions'
| 'config'
| 'config_subentries'
| 'config_panel'
| 'options'
| 'device_automation'
| 'mfa_setup'
| 'system_health'
| 'application_credentials'
| 'issues'
| 'selector'
| 'services';
/**
* Retrieves the translations for Home Assistant.
*
* @async
*
* @param {HomeAssistant} hass - The Home Assistant instance.
* @param {string} language - The language for translations.
* @param {TranslationCategory} category - The category of translations.
* @param {string | string[]} [integration] - Optional integration name(s).
* @param {boolean} [config_flow] - Optional flag for config flow.
*
* @returns {Promise<Record<string, unknown>>} A promise resolving to an object containing translation key-value pairs.
*/
export const getHassTranslations = async (
hass: HomeAssistant,
language: string,
category: TranslationCategory,
integration?: string | string[],
config_flow?: boolean,
): Promise<Record<string, unknown>> => {
const result = await hass.callWS<{ resources: Record<string, unknown> }>({
type: 'frontend/get_translations',
language,
category,
integration,
config_flow,
});
return result.resources;
};

View File

@ -0,0 +1,50 @@
/**
* Represents the variables for a theme in Home Assistant.
*
* @property {string} primary-color - The primary color of the theme.
* @property {string} text-primary-color - The primary text color of the theme.
* @property {string} accent-color - The accent color of the theme.
* @property {string} [key] - Additional theme variables as key-value pairs.
*/
export interface ThemeVars {
// Incomplete
'primary-color': string;
'text-primary-color': string;
'accent-color': string;
[key: string]: string;
}
/**
* Represents a theme configuration in Home Assistant.
*
* @property {ThemeVars} modes.light - The light mode variables.
* @property {ThemeVars} modes.dark - The dark mode variables.
*/
export type Theme = ThemeVars & {
modes?: {
light?: ThemeVars;
dark?: ThemeVars;
};
};
/**
* Represents the overall themes configuration in Home Assistant.
*
* @property {string} default_theme - The default theme name.
* @property {string | null} default_dark_theme - The default dark theme name or null.
* @property {Record<string, Theme>} themes - A record of available themes.
* @property {boolean} darkMode - Currently effective dark mode.
* It Will never be undefined.
* If the user selected "auto" in the theme picker, this property will still contain
* either true or false based on what has been determined via system preferences and
* support for the selected theme.
* @property {string} theme - Currently globally active theme name
*/
export interface Themes {
default_theme: string;
default_dark_theme: string | null;
themes: Record<string, Theme>;
darkMode: boolean;
theme: string;
}

View File

@ -1,26 +0,0 @@
import {LovelaceCardConfig} from "../../data/lovelace";
/**
* Home Assistant Stack Card Config.
*
* @property {string} type The stack type.
* @property {Object[]} cards The content of the stack.
*
* @see https://www.home-assistant.io/dashboards/horizontal-stack/
* @see https://www.home-assistant.io/dashboards/vertical-stack/
*/
export interface StackCardConfig extends LovelaceCardConfig {
cards: LovelaceCardConfig[];
title?: string;
}
/**
* Home Assistant Area Card Config.
*
* @see https://www.home-assistant.io/dashboards/area/
*/
export interface AreaCardConfig extends LovelaceCardConfig {
area: string;
navigation_path?: string;
show_camera?: boolean;
}

View File

@ -0,0 +1,91 @@
/** Represents a condition in Home Assistant. */
export type Condition =
| NumericStateCondition
| StateCondition
| ScreenCondition
| UserCondition
| OrCondition
| AndCondition;
/**
* Base interface for all conditions in Home Assistant.
*
* @property {string} condition - The type of condition.
*/
interface BaseCondition {
condition: string;
}
/**
* Represents a numeric state condition in Home Assistant.
*
* @property {'numeric_state'} condition - The condition type.
* @property {string} [entity] - The entity to evaluate.
* @property {string | number} [below] - The threshold value below which the condition is true.
* @property {string | number} [above] - The threshold value above which the condition is true.
*/
export interface NumericStateCondition extends BaseCondition {
condition: 'numeric_state';
entity?: string;
below?: string | number;
above?: string | number;
}
/**
* Represents a state condition in Home Assistant.
*
* @property {'state'} condition - The condition type.
* @property {string} [entity] - The entity to evaluate.
* @property {string | string[]} [state] - The expected state of the entity.
* @property {string | string[]} [state_not] - The state that the entity should not be in.
*/
export interface StateCondition extends BaseCondition {
condition: 'state';
entity?: string;
state?: string | string[];
state_not?: string | string[];
}
/**
* Represents a screen condition in Home Assistant.
*
* @property {'screen'} condition - The condition type.
* @property {string} [media_query] - The media query for screen conditions.
*/
export interface ScreenCondition extends BaseCondition {
condition: 'screen';
media_query?: string;
}
/**
* Represents a user condition in Home Assistant.
*
* @property {'user'} condition - The condition type.
* @property {string[]} [users] - The list of users for the condition.
*/
export interface UserCondition extends BaseCondition {
condition: 'user';
users?: string[];
}
/**
* Represents an OR condition in Home Assistant.
*
* @property {'or'} condition - The condition type.
* @property {Condition[]} [conditions] - The list of conditions to evaluate.
*/
export interface OrCondition extends BaseCondition {
condition: 'or';
conditions?: Condition[];
}
/**
* Represents an AND condition in Home Assistant.
*
* @property {'and'} condition - The condition type.
* @property {Condition[]} [conditions] - The list of conditions to evaluate.
*/
export interface AndCondition extends BaseCondition {
condition: 'and';
conditions?: Condition[];
}

View File

@ -0,0 +1,70 @@
import { ActionConfig } from '../../../data/lovelace/config/action';
import { LovelaceCardConfig } from '../../../data/lovelace/config/card';
/**
* Home Assistant Area Card Config.
*
* @property {string} area - The area associated with the card.
* @property {string} [navigation_path] - Optional navigation path for the card.
* @property {boolean} [show_camera] - Whether to show the camera view.
* @property {"live" | "auto"} [camera_view] - The camera view mode.
* @property {string} [aspect_ratio] - The aspect ratio of the card.
* @see https://www.home-assistant.io/dashboards/area/
*/
export interface AreaCardConfig extends LovelaceCardConfig {
area: string;
navigation_path?: string;
show_camera?: boolean;
camera_view?: 'live' | 'auto';
aspect_ratio?: string;
}
/**
* Home Assistant Picture Entity Config.
*
* @property {string} entity - An entity_id used for the picture.
* @property {string} [name] - Overwrite entity name.
* @property {string} [image] - URL of an image.
* @property {string} [camera_image] - Camera entity_id to use.
* @property {"live" | "auto"} [camera_view] - The camera view mode.
* @property {Record<string, unknown>} [state_image] - Map entity states to images.
* @property {string[]} [state_filter] - State-based CSS filters.
* @property {string} [aspect_ratio] - Forces the height of the image to be a ratio of the width.
* @property {ActionConfig} [tap_action] - Action taken on card tap.
* @property {ActionConfig} [hold_action] - Action taken on card tap and hold.
* @property {ActionConfig} [double_tap_action] - Action taken on card double tap.
* @property {boolean} [show_name=true] - Shows name in footer.
* @property {string} [theme=true] - Override the used theme for this card.
* @property {boolean} [show_state] - Shows state in footer.
* @see https://www.home-assistant.io/dashboards/picture-entity/
*/
export interface PictureEntityCardConfig extends LovelaceCardConfig {
entity: string;
name?: string;
image?: string;
camera_image?: string;
camera_view?: 'live' | 'auto';
state_image?: Record<string, unknown>;
state_filter?: string[];
aspect_ratio?: string;
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
show_name?: boolean;
show_state?: boolean;
theme?: string;
}
/**
* Home Assistant Stack Card Config.
*
* @property {string} type - The stack type.
* @property {Object[]} cards - The content of the stack.
* @see https://www.home-assistant.io/dashboards/horizontal-stack/
* @see https://www.home-assistant.io/dashboards/vertical-stack/
*/
export interface StackCardConfig extends LovelaceCardConfig {
type: string;
cards: LovelaceCardConfig[];
title?: string;
}

View File

@ -0,0 +1,37 @@
/**
* Represents the layout options for Lovelace in Home Assistant.
*
* @property {number | "full"} [grid_columns] - The number of grid columns or "full".
* @property {number | "auto"} [grid_rows] - The number of grid rows or "auto".
* @property {number} [grid_max_columns] - The maximum number of grid columns.
* @property {number} [grid_min_columns] - The minimum number of grid columns.
* @property {number} [grid_min_rows] - The minimum number of grid rows.
* @property {number} [grid_max_rows] - The maximum number of grid rows.
*/
export interface LovelaceLayoutOptions {
grid_columns?: number | 'full';
grid_rows?: number | 'auto';
grid_max_columns?: number;
grid_min_columns?: number;
grid_min_rows?: number;
grid_max_rows?: number;
}
/**
* Represents the grid options for Lovelace in Home Assistant.
*
* @property {number | "full"} [columns] - The number of columns or "full".
* @property {number | "auto"} [rows] - The number of rows or "auto".
* @property {number} [max_columns] - The maximum number of columns.
* @property {number} [min_columns] - The minimum number of columns.
* @property {number} [min_rows] - The minimum number of rows.
* @property {number} [max_rows] - The maximum number of rows.
*/
export interface LovelaceGridOptions {
columns?: number | 'full';
rows?: number | 'auto';
max_columns?: number;
min_columns?: number;
min_rows?: number;
max_rows?: number;
}

View File

@ -1,41 +0,0 @@
import {ActionConfig, LovelaceCardConfig} from "../../../data/lovelace";
/**
* Home Assistant Picture Entity Config.
*
* @property {string} entity An entity_id used for the picture.
* @property {string} [name] Overwrite entity name.
* @property {string} [image] URL of an image.
* @property {string} [camera_image] Camera entity_id to use. (not required if entity is already a camera-entity).
* @property {string} [camera_view=auto] “live” will show the live view if stream is enabled.
* @property {Record<string, unknown>} [state_image] Map entity states to images (state: image URL).
* @property {string[]} [state_filter] State-based CSS filters.
* @property {string} [aspect_ratio] Forces the height of the image to be a ratio of the width.
* Valid formats: Height percentage value (23%) or ratio expressed with colon or “x”
* separator (16:9 or 16x9).
* For a ratio, the second element can be omitted and will default to “1”
* (1.78 equals 1.78:1).
* @property {ActionConfig} [tap_action] Action taken on card tap.
* @property {ActionConfig} [hold_action] Action taken on card tap and hold.
* @property {ActionConfig} [double_tap_action] Action taken on card double tap.
* @property {boolean} [show_name=true] Shows name in footer.
* @property {string} [theme=true] Override the used theme for this card with any loaded theme.
*
* @see https://www.home-assistant.io/dashboards/picture-entity/
*/
export interface PictureEntityCardConfig extends LovelaceCardConfig {
entity: string;
name?: string;
image?: string;
camera_image?: string;
camera_view?: "live" | "auto";
state_image?: Record<string, unknown>;
state_filter?: string[];
aspect_ratio?: string;
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
show_name?: boolean;
show_state?: boolean;
theme?: string;
}

View File

@ -1,19 +1,56 @@
import {Auth, Connection, HassConfig, HassEntities, HassServices, MessageBase,} from "home-assistant-js-websocket";
import {AreaRegistryEntry} from "./data/area_registry";
import {DeviceRegistryEntry} from "./data/device_registry";
import {EntityRegistryDisplayEntry} from "./data/entity_registry";
import {
Auth,
Connection,
HassConfig,
HassEntities,
HassEntity,
HassServices,
HassServiceTarget,
MessageBase,
} from 'home-assistant-js-websocket';
import { LocalizeFunc } from './common/translations/localize';
import { AreaRegistryEntry } from './data/area_registry';
import { DeviceRegistryEntry } from './data/device_registry';
import { EntityRegistryDisplayEntry } from './data/entity_registry';
import { FloorRegistryEntry } from './data/floor_registry';
import { CoreFrontendUserData } from './data/frontend';
import { FrontendLocaleData, getHassTranslations } from './data/translations';
import { Themes } from './data/ws-themes';
/**
* Represents the credentials for a user in Home Assistant.
*
* @property {string} auth_provider_type - The type of authentication provider.
* @property {string} auth_provider_id - The ID of the authentication provider.
*/
export interface Credential {
auth_provider_type: string;
auth_provider_id: string;
}
/**
* Represents a multifactor authentication module in Home Assistant.
*
* @property {string} id - The unique identifier for the MFA module.
* @property {string} name - The name of the MFA module.
* @property {boolean} enabled - Whether the MFA module is enabled.
*/
export interface MFAModule {
id: string;
name: string;
enabled: boolean;
}
/**
* Represents the current user in Home Assistant.
*
* @property {string} id - The unique identifier for the user.
* @property {boolean} is_owner - Indicates if the user is an owner.
* @property {boolean} is_admin - Indicates if the user is an admin.
* @property {string} name - The name of the user.
* @property {Credential[]} credentials - The credentials associated with the user.
* @property {MFAModule[]} mfa_modules - The MFA modules associated with the user.
*/
export interface CurrentUser {
id: string;
is_owner: boolean;
@ -23,6 +60,18 @@ export interface CurrentUser {
mfa_modules: MFAModule[];
}
/**
* Represents information about a panel in Home Assistant.
*
* @template T The type of the configuration object for the panel.
*
* @property {string} component_name - The name of the component for the panel.
* @property {T} config - The configuration for the panel.
* @property {string | null} icon - The icon for the panel.
* @property {string | null} title - The title of the panel.
* @property {string} url_path - The URL path for the panel.
* @property {string} [config_panel_domain] - The domain for the configuration panel.
*/
export interface PanelInfo<T = Record<string, any> | null> {
component_name: string;
config: T;
@ -32,64 +81,305 @@ export interface PanelInfo<T = Record<string, any> | null> {
config_panel_domain?: string;
}
/**
* Represents the panels in Home Assistant.
*
* @property {Record<string, PanelInfo>} panels - The panel configurations.
*/
export interface Panels {
[name: string]: PanelInfo;
panels: Record<string, PanelInfo>;
}
/**
* Represents a translation in Home Assistant.
*
* @property {string} nativeName - The native name of the language.
* @property {boolean} isRTL - Indicates if the language is written right-to-left.
* @property {string} hash - The hash for the translation.
*/
export interface Translation {
nativeName: string;
isRTL: boolean;
hash: string;
}
/**
* Represents metadata for translations in Home Assistant.
*
* @property {string[]} fragments - The fragments of the translation.
* @property {Record<string, Translation>} translations - The translations mapped by language.
*/
export interface TranslationMetadata {
fragments: string[];
translations: {
[lang: string]: Translation;
};
translations: Record<string, Translation>;
}
/**
* Represents a dictionary of translations in Home Assistant.
*
* @property {Record<string, string>} translations - The translations mapped by a key.
*/
export interface TranslationDict {
translations: Record<string, string>;
}
/**
* Represents resources in Home Assistant.
*
* @property {Record<string, Record<string, string>>} resources - The resources mapped by a key.
*/
export interface Resources {
[language: string]: Record<string, string>;
resources: Record<string, Record<string, string>>;
}
/**
* Represents the settings for themes in Home Assistant.
*
* @property {string} theme - The name of the selected theme.
* @property {boolean} [dark] - Indicates if the theme is dark.
* @property {string} [primaryColor] - The primary color of the theme.
* @property {string} [accentColor] - The accent color of the theme.
*/
export interface ThemeSettings {
theme: string;
dark?: boolean;
primaryColor?: string;
accentColor?: string;
}
/**
* Represents the main Home Assistant object.
*
* @interface HomeAssistant
* @property {Auth} auth - The authentication object.
* @property {Connection} connection - The connection object.
* @property {boolean} connected - Indicates if the connection is active.
* @property {HassEntities} states - The current states of entities.
* @property {Record<string, EntityRegistryDisplayEntry>} entities - The entities in the registry.
* @property {Record<string, DeviceRegistryEntry>} devices - The devices in the registry.
* @property {Record<string, AreaRegistryEntry>} areas - The areas in the registry.
* @property {Record<string, FloorRegistryEntry>} floors - The floors in the registry.
* @property {HassServices} services - The services available in Home Assistant.
* @property {HassConfig} config - The configuration for Home Assistant.
* @property {Themes} themes - The available themes.
* @property {ThemeSettings | null} selectedTheme - The currently selected theme.
* @property {Panels} panels - The panel configurations.
* @property {string} panelUrl - The URL for the panel.
* @property {string} language - The current language.
* @property {string | null} selectedLanguage - The selected language.
* @property {FrontendLocaleData} locale - The locale data.
* @property {Resources} resources - The resources available.
* @property {LocalizeFunc} localize - The localization function.
* @property {TranslationMetadata} translationMetadata - The translation metadata.
* @property {boolean} suspendWhenHidden - Indicates if the frontend should suspend when hidden.
* @property {boolean} enableShortcuts - Indicates if shortcuts are enabled.
* @property {boolean} vibrate - Indicates if vibration feedback is enabled.
* @property {boolean} debugConnection - Indicates if debug mode is enabled for the connection.
* @property {'docked' | 'always_hidden' | 'auto'} dockedSidebar - The sidebar visibility setting.
* @property {string} defaultPanel - The default panel to show.
* @property {string | null} moreInfoEntityId - The entity ID for more info.
* @property {CurrentUser} [user] - The current user object.
* @property {CoreFrontendUserData | null} [userData] - The frontend user data.
*/
export interface HomeAssistant {
auth: Auth & { external?: any };
auth: Auth & { external?: { [key: string]: any } };
connection: Connection;
connected: boolean;
states: HassEntities;
entities: { [id: string]: EntityRegistryDisplayEntry };
devices: { [id: string]: DeviceRegistryEntry };
areas: { [id: string]: AreaRegistryEntry };
entities: Record<string, EntityRegistryDisplayEntry>;
devices: Record<string, DeviceRegistryEntry>;
areas: Record<string, AreaRegistryEntry>;
floors: Record<string, FloorRegistryEntry>;
services: HassServices;
config: HassConfig;
themes: { [k: string]: any };
selectedTheme: { [k: string]: any } | null;
themes: Themes;
selectedTheme: ThemeSettings | null;
panels: Panels;
panelUrl: string;
// i18n
// current effective language in that order:
// - backend saved user selected language
// - language in local app storage
// - browser language
// - english (en)
language: string;
// local stored language, keep that name for backward compatibility
selectedLanguage: string | null;
locale: { [k: string]: any };
locale: FrontendLocaleData;
resources: Resources;
localize: Function;
localize: LocalizeFunc;
translationMetadata: TranslationMetadata;
suspendWhenHidden: boolean;
enableShortcuts: boolean;
vibrate: boolean;
debugConnection: boolean;
dockedSidebar: "docked" | "always_hidden" | "auto";
dockedSidebar: 'docked' | 'always_hidden' | 'auto';
defaultPanel: string;
moreInfoEntityId: string | null;
user?: CurrentUser;
userData?: { [k: string]: any } | null;
userData?: CoreFrontendUserData | null;
/**
* Returns the URL for the Home Assistant instance.
*
* @param {any} path - Optional path to append to the base URL.
*/
hassUrl(path?: any): string;
/**
* Calls a service in Home Assistant.
*
* @param {ServiceCallRequest['domain']} domain - The domain of the service.
* @param {ServiceCallRequest['service']} service - The name of the service to call.
* @param {ServiceCallRequest['serviceData']} [serviceData] - Optional data to send with the service call.
* @param {ServiceCallRequest['target']} [target] - Optional target for the service call.
* @param {boolean} [notifyOnError] - Whether to notify on error.
* @param {boolean} [returnResponse] - Whether to return the response.
*/
callService(
domain: ServiceCallRequest['domain'],
service: ServiceCallRequest['service'],
serviceData?: ServiceCallRequest['serviceData'],
target?: ServiceCallRequest['target'],
notifyOnError?: boolean,
returnResponse?: boolean,
): Promise<ServiceCallResponse>;
/**
* Calls the Home Assistant API.
*
* @template T The expected response type.
*
* @param {'GET' | 'POST' | 'PUT' | 'DELETE'} method - The HTTP method to use.
* @param {string} path - The API endpoint path.
* @param {Record<string, any>} [parameters] - Optional parameters to send with the request.
* @param {Record<string, string>} [headers] - Optional headers to include in the request.
*/
callApi<T>(
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
path: string,
parameters?: Record<string, any>,
headers?: Record<string, string>,
): Promise<T>;
/**
* Calls the Home Assistant API with raw response.
*
* @param {'GET' | 'POST' | 'PUT' | 'DELETE'} method - The HTTP method to use.
* @param {string} path - The API endpoint path.
* @param {Record<string, any>} [parameters] - Optional parameters to send with the request.
* @param {Record<string, string>} [headers] - Optional headers to include in the request.
* @param {AbortSignal} [signal] - Optional signal to abort the request.
*/
callApiRaw(
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
path: string,
parameters?: Record<string, any>,
headers?: Record<string, string>,
signal?: AbortSignal,
): Promise<Response>;
/**
* Fetches a resource with authentication.
*
* @param {string} path - The resource path to fetch.
* @param {Record<string, any>} [init] - Optional fetch options.
*/
fetchWithAuth(path: string, init?: Record<string, any>): Promise<Response>;
/**
* Sends a WebSocket message.
*
* @param {MessageBase} msg - The message to send.
*/
sendWS(msg: MessageBase): void;
/**
* Calls a WebSocket service.
*
* @template T The expected response type.
*
* @param {MessageBase} msg - The message to send.
*/
callWS<T>(msg: MessageBase): Promise<T>;
/**
* Load backend translation.
*
* @param {Parameters<typeof getHassTranslations>[2]} category - The category of translations.
* @param {Parameters<typeof getHassTranslations>[3]} [integrations] - Optional integrations to include.
* @param {Parameters<typeof getHassTranslations>[4]} [configFlow] - Optional config flow.
*
* @returns {Promise<LocalizeFunc>} The localization function.
*/
loadBackendTranslation(
category: Parameters<typeof getHassTranslations>[2],
integrations?: Parameters<typeof getHassTranslations>[3],
configFlow?: Parameters<typeof getHassTranslations>[4],
): Promise<LocalizeFunc>;
/**
* Load fragment translation.
*
* @param {string} fragment - The fragment to load.
* @returns {Promise<LocalizeFunc | undefined>} The localization function or undefined.
*/
loadFragmentTranslation(fragment: string): Promise<LocalizeFunc | undefined>;
/**
* Formats the state of an entity.
*
* @param {HassEntity} stateObj - The state object of the entity.
* @param {string} [state] - Optional state to format.
*/
formatEntityState(stateObj: HassEntity, state?: string): string;
/**
* Formats the value of an entity attribute.
*
* @param {HassEntity} stateObj - The state object of the entity.
* @param {string} attribute - The attribute to format.
* @param {any} [value] - Optional value to format.
*/
formatEntityAttributeValue(stateObj: HassEntity, attribute: string, value?: any): string;
/**
* Formats the name of an entity attribute.
*
* @param {HassEntity} stateObj - The state object of the entity.
* @param {string} attribute - The attribute to format.
*/
formatEntityAttributeName(stateObj: HassEntity, attribute: string): string;
}
/**
* Represents the context of a service call.
*
* @property {string} id - The unique identifier for the context.
* @property {string} [parent_id] - The optional parent ID of the context.
* @property {string | null} [user_id] - The optional user ID associated with the context.
*/
export interface Context {
id: string;
parent_id?: string;
user_id?: string | null;
}
/**
* Represents a service call request in Home Assistant.
*
* @property {string} domain - The domain of the service to call.
* @property {string} service - The name of the service to call.
* @property {Record<string, any>} [serviceData] - Optional data to send with the service call.
* @property {HassServiceTarget} [target] - Optional target for the service call.
*/
export interface ServiceCallRequest {
domain: string;
service: string;
serviceData?: Record<string, any>;
target?: HassServiceTarget;
}
/**
* Represents the response from a service call in Home Assistant.
*
* @property {Context} context - The context of the service call.
* @property {any} [response] - The optional response data from the service call.
*/
export interface ServiceCallResponse {
context: Context;
response?: any;
}

View File

@ -1,11 +1,11 @@
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {LovelaceChipConfig} from "../utils/lovelace/chip/types";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { LovelaceChipConfig } from '../utils/lovelace/chip/types';
/**
* Chips Card Configuration
*
* @param {LovelaceChipConfig[]} chips Chips Array
* @param {string} [alignment=start] Chips alignment (end, center, justify), when empty default behavior is start.
* @property {LovelaceChipConfig[]} chips - Array of chips to display.
* @property {string} [alignment] - Chips alignment (start, end, center, justify). Defaults to 'start'.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/chips.md
*/

View File

@ -1,15 +1,16 @@
import {HvacMode} from "../../homeassistant/data/climate";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import {ActionsSharedConfig} from "../shared/config/actions-config";
import { HvacMode } from '../../homeassistant/data/climate';
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/**
* Climate Card Config.
* Climate Card Configuration
*
* @property {boolean} [show_temperature_control=false] Show buttons to control target temperature.
* @property {HvacMode[]} [hvac_modes] List of hvac modes to display (auto, heat_cool, heat, cool, dry, fan_only, off).
* @property {boolean} [collapsible_controls] Collapse controls when off.
* @property {boolean} [show_temperature_control] - Show buttons to control target temperature. Defaults to false.
* @property {HvacMode[]} [hvac_modes] - List of HVAC modes to display.
* (auto, heat_cool, heat, cool, dry, fan_only, off).
* @property {boolean} [collapsible_controls] - Collapse controls when off.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/climate.md
*/
@ -17,7 +18,7 @@ export type ClimateCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
show_temperature_control?: boolean;
hvac_modes?: HvacMode[];
collapsible_controls?: boolean;
};
show_temperature_control?: boolean;
hvac_modes?: HvacMode[];
collapsible_controls?: boolean;
};

View File

@ -1,14 +1,15 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/**
* Cover Card Config.
* Cover Card Configuration
*
* @property {boolean} [show_buttons_control=false] Show buttons to open, close and stop cover.
* @property {boolean} [show_position_control=false] Show a slider to control position of the cover.
* @property {boolean} [show_tilt_position_control=false] Show a slider to control tilt position of the cover.
* @property {boolean} [show_buttons_control] - Show buttons to open, close, and stop the cover. Defaults to false.
* @property {boolean} [show_position_control] - Show a slider to control the position of the cover. Defaults to false.
* @property {boolean} [show_tilt_position_control] - Show a slider to control the tilt position of the cover. Defaults
* to false.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/cover.md
*/
@ -16,7 +17,7 @@ export type CoverCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
show_buttons_control?: boolean;
show_position_control?: boolean;
show_tilt_position_control?: boolean;
};
show_buttons_control?: boolean;
show_position_control?: boolean;
show_tilt_position_control?: boolean;
};

View File

@ -1,12 +1,12 @@
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {ActionsSharedConfig} from "../shared/config/actions-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/**
* Entity Card Config.
* Entity Card Configuration
*
* @property {string} [icon_color=blue] Custom color for icon when entity is state is active.
* @property {string} [icon_color] - Custom color for the icon when the entity's state is active. Defaults to 'blue'.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/entity.md
*/
@ -14,5 +14,5 @@ export type EntityCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
icon_color?: string;
};
icon_color?: string;
};

View File

@ -1,15 +1,15 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/**
* Fan Card Config.
* Fan Card Configuration
*
* @property {boolean} [icon_animation=false] Animate the icon when fan is on.
* @property {boolean} [show_percentage_control=false] Show a slider to control speed.
* @property {boolean} [show_oscillate_control=false] Show a button to control oscillation.
* @property {boolean} [icon_animation=false] Animate the icon when fan is on.
* @property {boolean} [icon_animation] - Animate the icon when the fan is on. Defaults to false.
* @property {boolean} [show_percentage_control] - Show a slider to control speed. Defaults to false.
* @property {boolean} [show_oscillate_control] - Show a button to control oscillation. Defaults to false.
* @property {boolean} [show_direction_control] - Show a button to control the direction. Defaults to false.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/fan.md
*/
@ -17,8 +17,8 @@ export type FanCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
icon_animation?: boolean;
show_percentage_control?: boolean;
show_oscillate_control?: boolean;
collapsible_controls?: boolean;
};
icon_animation?: boolean;
show_percentage_control?: boolean;
show_oscillate_control?: boolean;
show_direction_control?: boolean;
};

View File

@ -1,18 +1,19 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/**
* Light Card Config.
* Light Card Configuration
*
* @property {string} [icon_color=blue] Custom color for icon and brightness bar when the lights are on and
* use_light_color is false.
* @property {boolean} [show_brightness_control=false] Show a slider to control brightness.
* @property {boolean} [show_color_temp_control=false] Show a slider to control temperature color.
* @property {boolean} [show_color_control=false] Show a slider to control RGB color.
* @property {boolean} [collapsible_controls=false] Collapse controls when off.
* @property {boolean} [use_light_color=false] Colorize the icon and slider according light temperature or color.
* @property {string} [icon_color] - Custom color for icon and brightness bar when the lights are on and
* `use_light_color` is false; Defaults to 'blue'.
* @property {boolean} [show_brightness_control] - Show a slider to control brightness. Defaults to false.
* @property {boolean} [show_color_temp_control] - Show a slider to control temperature color; Defaults to false.
* @property {boolean} [show_color_control] - Show a slider to control RGB color. Defaults to false.
* @property {boolean} [collapsible_controls] - Collapse controls when off; Defaults to false.
* @property {boolean} [use_light_color] - Colorize the icon and slider according to light temperature or color.
* Defaults to false.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/light.md
*/
@ -20,10 +21,10 @@ export type LightCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
icon_color?: string;
show_brightness_control?: boolean;
show_color_temp_control?: boolean;
show_color_control?: boolean;
collapsible_controls?: boolean;
use_light_color?: boolean;
};
icon_color?: string;
show_brightness_control?: boolean;
show_color_temp_control?: boolean;
show_color_control?: boolean;
collapsible_controls?: boolean;
use_light_color?: boolean;
};

View File

@ -1,14 +1,11 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/**
* Lock Card Config.
* Lock Card Configuration
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/lock.md
*/
export type LockCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig;
export type LockCardConfig = LovelaceCardConfig & EntitySharedConfig & AppearanceSharedConfig & ActionsSharedConfig;

View File

@ -1,35 +1,37 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/** List of available media controls. */
export const MEDIA_LAYER_MEDIA_CONTROLS = [
"on_off",
"shuffle",
"previous",
"play_pause_stop",
"next",
"repeat",
'on_off',
'shuffle',
'previous',
'play_pause_stop',
'next',
'repeat',
] as const;
/** Represents a single media control option. */
export type MediaPlayerMediaControl = (typeof MEDIA_LAYER_MEDIA_CONTROLS)[number];
export const MEDIA_PLAYER_VOLUME_CONTROLS = [
"volume_mute",
"volume_set",
"volume_buttons",
] as const;
/** List of available volume controls. */
export const MEDIA_PLAYER_VOLUME_CONTROLS = ['volume_mute', 'volume_set', 'volume_buttons'] as const;
/** Represents a single volume control option. */
export type MediaPlayerVolumeControl = (typeof MEDIA_PLAYER_VOLUME_CONTROLS)[number];
/**
* Media Player Card Config.
* Media Player Card Configuration.
*
* @property {boolean} [use_media_info=false] Use media info instead of name, state, and icon when media is playing.
* @property {boolean} [show_volume_level=false] Show volume level next to media state when media is playing.
* @property {MediaPlayerVolumeControl[]} [volume_controls] List of controls to display (volume_mute, volume_set, volume_buttons)
* @property {MediaPlayerMediaControl[]} [media_controls] List of controls to display (on_off, shuffle, previous, play_pause_stop, next, repeat)
* @property {boolean} [collapsible_controls=false] Collapse controls when off
* @property {boolean} [use_media_info] - Use media info instead of name, state, and icon when media is playing.
* Defaults to false.
* @property {boolean} [show_volume_level] - Show volume level next to media state when media is playing.
* Defaults to false.
* @property {MediaPlayerVolumeControl[]} [volume_controls] - List of controls to display.
* (volume_mute, volume_set, volume_buttons)
* @property {MediaPlayerMediaControl[]} [media_controls] - List of controls to display
* (on_off, shuffle, previous, play_pause_stop, next, repeat)
* @property {boolean} [collapsible_controls] - Collapse controls when off; Defaults to false.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/media-player.md
*/
@ -37,9 +39,9 @@ export type MediaPlayerCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
use_media_info?: boolean;
show_volume_level?: boolean;
volume_controls?: MediaPlayerVolumeControl[];
media_controls?: MediaPlayerMediaControl[];
collapsible_controls?: boolean;
};
use_media_info?: boolean;
show_volume_level?: boolean;
volume_controls?: MediaPlayerVolumeControl[];
media_controls?: MediaPlayerMediaControl[];
collapsible_controls?: boolean;
};

View File

@ -1,17 +1,17 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
export const DISPLAY_MODES = ["slider", "buttons"] as const;
export const DISPLAY_MODES = ['slider', 'buttons'] as const;
type DisplayMode = (typeof DISPLAY_MODES)[number];
/**
* Number Card Config.
* Number Card Configuration
*
* @property {string} [icon_color=blue] Custom color for icon when entity state is active.
* @property {DisplayMode} [display_mode=slider] Slider or Button controls.
* @property {string} [icon_color] - Custom color for the icon when the entity state is active. Defaults to 'blue'.
* @property {DisplayMode} [display_mode] - Slider or Button controls. Defaults to 'slider'.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/number.md
*/
@ -19,6 +19,6 @@ export type NumberCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
icon_color?: string;
display_mode?: DisplayMode;
};
icon_color?: string;
display_mode?: DisplayMode;
};

View File

@ -1,14 +1,11 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/**
* Person Card Config.
* Person Card Configuration
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/person.md
*/
export type PersonCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig;
export type PersonCardConfig = LovelaceCardConfig & EntitySharedConfig & AppearanceSharedConfig & ActionsSharedConfig;

View File

@ -1,12 +1,10 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {EntitySharedConfig} from "../shared/config/entity-config";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
import { EntitySharedConfig } from '../shared/config/entity-config';
/**
* Select Card Config.
*
* @property {string} [icon_color=blue] Custom color for icon when entity state is active.
* Select Card Configuration
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/select.md
*/
@ -14,5 +12,5 @@ export type SelectCardConfig = LovelaceCardConfig &
EntitySharedConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
icon_color?: string;
};
icon_color?: string;
};

View File

@ -1,36 +1,35 @@
import {ActionsSharedConfig} from "../shared/config/actions-config";
import {LovelaceCardConfig} from "../../homeassistant/data/lovelace";
import {AppearanceSharedConfig} from "../shared/config/appearance-config";
import { LovelaceCardConfig } from '../../homeassistant/data/lovelace/config/card';
import { ActionsSharedConfig } from '../shared/config/actions-config';
import { AppearanceSharedConfig } from '../shared/config/appearance-config';
/**
* Template Card Config.
* Template Card Configuration
*
* @property {string} [entity]
* @property {string} [icon] Icon to render. May contain templates.
* @property {string} [icon_color] Icon color to render. May contain templates.
* @property {string} [primary] Primary info to render. May contain templates.
* @property {string} [secondary] Secondary info to render. May contain templates.
* @property {string} [badge_icon] Badge icon to render. May contain templates.
* @property {string} [badge_color] Badge icon color to render. May contain templates.
* @property {string} [picture] Picture to render. May contain templates.
* @property {boolean} [multiline_secondary] Enables support for multiline text for the secondary info.
* @property {string | string[]} [entity_id] Only reacts to the state changes of these entities.
* This can be used if the automatic analysis fails to find all relevant
* entities.
* @property {string} [entity] - Entity associated with the card.
* @property {string} [icon] - Icon to render. May contain templates.
* @property {string} [icon_color] - Icon color to render. May contain templates.
* @property {string} [primary] - Primary info to render. May contain templates.
* @property {string} [secondary] - Secondary info to render. May contain templates.
* @property {string} [badge_icon] - Badge icon to render. May contain templates.
* @property {string} [badge_color] - Badge icon color to render. May contain templates.
* @property {string} [picture] - The picture to render. May contain templates.
* @property {boolean} [multiline_secondary] - Enables support for multiline text for the secondary info.
* @property {string | string[]} [entity_id] - Only reacts to the state changes of these entities. This can be used if
* the automatic analysis fails to find all relevant entities.
*
* @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/template.md
*/
export type TemplateCardConfig = LovelaceCardConfig &
AppearanceSharedConfig &
ActionsSharedConfig & {
entity?: string;
icon?: string;
icon_color?: string;
primary?: string;
secondary?: string;
badge_icon?: string;
badge_color?: string;
picture?: string;
multiline_secondary?: boolean;
entity_id?: string | string[];
};
entity?: string;
icon?: string;
icon_color?: string;
primary?: string;
secondary?: string;
badge_icon?: string;
badge_color?: string;
picture?: string;
multiline_secondary?: boolean;
entity_id?: string | string[];
};

Some files were not shown because too many files have changed in this diff Show More