diff --git a/src/components/entity/state-info.js b/src/components/entity/state-info.js
deleted file mode 100644
index d228de67de..0000000000
--- a/src/components/entity/state-info.js
+++ /dev/null
@@ -1,152 +0,0 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-import "@polymer/paper-tooltip/paper-tooltip";
-/* eslint-plugin-disable lit */
-import LocalizeMixin from "../../mixins/localize-mixin";
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-import { computeStateName } from "../../common/entity/compute_state_name";
-import { computeRTL } from "../../common/util/compute_rtl";
-import "../ha-relative-time";
-import "./state-badge";
-
-class StateInfo extends LocalizeMixin(PolymerElement) {
- static get template() {
- return html`
- ${this.styleTemplate} ${this.stateBadgeTemplate} ${this.infoTemplate}
- `;
- }
-
- static get styleTemplate() {
- return html`
-
- `;
- }
-
- static get stateBadgeTemplate() {
- return html` `;
- }
-
- static get infoTemplate() {
- return html`
-
-
- [[computeStateName(stateObj)]]
-
-
-
-
-
-
-
-
- [[localize('ui.dialogs.more_info_control.last_changed')]]:
-
-
-
-
-
- [[localize('ui.dialogs.more_info_control.last_updated')]]:
-
-
-
-
-
-
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: Object,
- stateObj: Object,
- inDialog: {
- type: Boolean,
- value: () => false,
- },
- rtl: {
- type: Boolean,
- reflectToAttribute: true,
- computed: "computeRTL(hass)",
- },
- };
- }
-
- computeStateName(stateObj) {
- return computeStateName(stateObj);
- }
-
- computeRTL(hass) {
- return computeRTL(hass);
- }
-}
-
-customElements.define("state-info", StateInfo);
diff --git a/src/components/entity/state-info.ts b/src/components/entity/state-info.ts
new file mode 100644
index 0000000000..91991d5e22
--- /dev/null
+++ b/src/components/entity/state-info.ts
@@ -0,0 +1,158 @@
+import "@polymer/paper-tooltip/paper-tooltip";
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ LitElement,
+ property,
+ TemplateResult,
+} from "lit-element";
+import type { HassEntity } from "home-assistant-js-websocket";
+
+import { computeStateName } from "../../common/entity/compute_state_name";
+import { computeRTL } from "../../common/util/compute_rtl";
+import type { HomeAssistant } from "../../types";
+
+import "../ha-relative-time";
+import "./state-badge";
+
+@customElement("state-info")
+class StateInfo extends LitElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property({ attribute: false }) public stateObj?: HassEntity;
+
+ @property({ type: Boolean }) public inDialog = false;
+
+ // property used only in css
+ @property({ type: Boolean, reflect: true }) public rtl = false;
+
+ protected render(): TemplateResult {
+ if (!this.hass || !this.stateObj) {
+ return html``;
+ }
+
+ return html`
+
+
+ ${computeStateName(this.stateObj)}
+
+ ${this.inDialog
+ ? html`
+
+
+
+
+
+ ${this.hass.localize(
+ "ui.dialogs.more_info_control.last_changed"
+ )}:
+
+
+
+
+
+ ${this.hass.localize(
+ "ui.dialogs.more_info_control.last_updated"
+ )}:
+
+
+
+
+
+
`
+ : html``}
+
`;
+ }
+
+ protected updated(changedProps) {
+ super.updated(changedProps);
+ if (!changedProps.has("hass")) {
+ return;
+ }
+
+ const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
+ if (!oldHass || oldHass.language !== this.hass.language) {
+ this.rtl = computeRTL(this.hass);
+ }
+ }
+
+ static get styles(): CSSResult {
+ return css`
+ :host {
+ @apply --paper-font-body1;
+ min-width: 120px;
+ white-space: nowrap;
+ }
+
+ state-badge {
+ float: left;
+ }
+
+ :host([rtl]) state-badge {
+ float: right;
+ }
+
+ .info {
+ margin-left: 56px;
+ }
+
+ :host([rtl]) .info {
+ margin-right: 56px;
+ margin-left: 0;
+ text-align: right;
+ }
+
+ .name {
+ @apply --paper-font-common-nowrap;
+ color: var(--primary-text-color);
+ line-height: 40px;
+ }
+
+ .name[in-dialog],
+ :host([secondary-line]) .name {
+ line-height: 20px;
+ }
+
+ .time-ago,
+ .extra-info,
+ .extra-info > * {
+ @apply --paper-font-common-nowrap;
+ color: var(--secondary-text-color);
+ }
+
+ .row {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: no-wrap;
+ width: 100%;
+ justify-content: space-between;
+ margin: 0 2px 4px 0;
+ }
+
+ .row:last-child {
+ margin-bottom: 0px;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "state-info": StateInfo;
+ }
+}