diff --git a/src/components/header-slide.js b/src/components/header-slide.js
deleted file mode 100644
index 0fccc37..0000000
--- a/src/components/header-slide.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import { LitElement, css, html } from 'lit';
-import Swiper from 'swiper';
-import { Pagination } from 'swiper/modules';
-import swipercss from '../css/swiper-bundle.css';
-
-export class HeaderSlide extends LitElement {
- static get properties() {
- return {
- images: { Type: Array },
- swiper: { Type: Object },
- };
- }
-
- static get styles() {
- return [
- swipercss,
- css`
- :host {
- --swiper-pagination-bottom: 0px;
- --swiper-theme-color: var(--primary-text-color);
- }
- section {
- display: block;
- padding: 1rem 0;
- }
- .swiper-container {
- width: 100%;
- height: 100%;
- display: block;
- }
- .swiper-slide {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- max-height: 125px;
- }
- .swiper-slide:active {
- scale: 1.02;
- }
-
- .swiper-slide img {
- width: 100%;
- height: 100%;
- max-height: 150px;
- object-fit: scale-down;
- }
- .swiper-pagination-bullet {
- background-color: var(--swiper-theme-color);
- transition: all 0.3s ease-in-out !important;
- }
- .swiper-pagination-bullet-active {
- width: 18px !important;
- border-radius: 1rem !important;
- opacity: 0.7;
- }
- `,
- ];
- }
-
- firstUpdated() {
- if (!this.swiper) {
- this.initSwiper();
- }
- }
-
- initSwiper() {
- const swiperCon = this.shadowRoot.querySelector('.swiper-container');
- if (!swiperCon) return;
- const paginationEl = swiperCon.querySelector('.swiper-pagination');
- this.swiper = new Swiper(swiperCon, {
- modules: [Pagination],
- centeredSlides: true,
- grabCursor: true,
- speed: 500,
- roundLengths: true,
- keyboard: {
- enabled: true,
- onlyInViewport: true,
- },
- loop: true,
- slidesPerView: 1,
- pagination: {
- el: paginationEl,
- clickable: true,
- },
- });
- }
-
- render() {
- const images = this.images;
- if (!images || images.length === 0) {
- return html``;
- }
- return html`
-
-
-
- ${images.map(
- (image) => html`
-
-
-
- `,
- )}
-
-
-
-
- `;
- }
-}
-customElements.define('header-slide', HeaderSlide);
diff --git a/src/components/header-slide.ts b/src/components/header-slide.ts
new file mode 100644
index 0000000..a6354d0
--- /dev/null
+++ b/src/components/header-slide.ts
@@ -0,0 +1,110 @@
+import { LitElement, css, html, TemplateResult } from 'lit';
+import { customElement, property } from 'lit/decorators';
+import Swiper from 'swiper';
+import { Pagination } from 'swiper/modules';
+import swipercss from '../css/swiper-bundle.css';
+
+@customElement('header-slide')
+export class HeaderSlide extends LitElement {
+ @property({ type: Array })
+ images: string[] = [];
+
+ @property({ type: Object })
+ swiper: Swiper | null = null;
+
+ static styles = [
+ swipercss,
+ css`
+ :host {
+ --swiper-pagination-bottom: 0px;
+ --swiper-theme-color: var(--primary-text-color);
+ }
+ section {
+ display: block;
+ padding: 1rem 0;
+ }
+ .swiper-container {
+ width: 100%;
+ height: 100%;
+ display: block;
+ }
+ .swiper-slide {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ max-height: 125px;
+ }
+ .swiper-slide:active {
+ scale: 1.02;
+ }
+ .swiper-slide img {
+ width: 100%;
+ height: 100%;
+ max-height: 150px;
+ object-fit: scale-down;
+ }
+ .swiper-pagination-bullet {
+ background-color: var(--swiper-theme-color);
+ transition: all 0.3s ease-in-out !important;
+ }
+ .swiper-pagination-bullet-active {
+ width: 18px !important;
+ border-radius: 1rem !important;
+ opacity: 0.7;
+ }
+ `,
+ ];
+
+ firstUpdated(): void {
+ if (!this.swiper) {
+ this.initSwiper();
+ }
+ }
+
+ initSwiper(): void {
+ const swiperCon = this.shadowRoot?.querySelector('.swiper-container');
+ if (!swiperCon) return;
+ const paginationEl = swiperCon.querySelector('.swiper-pagination') as HTMLElement;
+ this.swiper = new Swiper(swiperCon as HTMLElement, {
+ modules: [Pagination],
+ centeredSlides: true,
+ grabCursor: true,
+ speed: 500,
+ roundLengths: true,
+ keyboard: {
+ enabled: true,
+ onlyInViewport: true,
+ },
+ loop: true,
+ slidesPerView: 1,
+ pagination: {
+ el: paginationEl,
+ clickable: true,
+ },
+ });
+ }
+
+ render(): TemplateResult {
+ const images = this.images;
+ if (!images || images.length === 0) {
+ return html``;
+ }
+ return html`
+
+
+
+ ${images.map(
+ (image) => html`
+
+
+
+ `,
+ )}
+
+
+
+
+ `;
+ }
+}
diff --git a/src/components/map-card.js b/src/components/map-card.ts
similarity index 75%
rename from src/components/map-card.js
rename to src/components/map-card.ts
index 54383b1..b7d243d 100644
--- a/src/components/map-card.js
+++ b/src/components/map-card.ts
@@ -1,68 +1,63 @@
-import { LitElement, html, css } from 'lit';
+import { LitElement, html, css, TemplateResult, CSSResultGroup } from 'lit';
+import { customElement, property, state } from 'lit/decorators.js';
+
+import { HomeAssistant } from 'custom-card-helpers';
import L from 'leaflet';
import 'leaflet-providers/leaflet-providers.js';
-
import mapstyle from '../css/leaflet.css';
-export class VehicleMap extends LitElement {
- static get properties() {
- return {
- hass: {},
- deviceTracker: { Type: String },
- lat: { Type: Number },
- lon: { Type: Number },
- zoom: { Type: Number },
- picture: { Type: String },
- map: { Type: Object },
- marker: { Type: Object },
- address: { Type: Object },
- darkMode: { Type: Boolean },
- state: { Type: String },
- apiKey: { Type: String },
- popup: { Type: Boolean },
- enableAdress: { Type: Boolean },
- };
- }
- constructor() {
- super();
- this.lat = 0;
- this.lon = 0;
- this.zoom = 16;
- this.picture = '';
- this.state = '';
- this.address = {};
- this.darkMode = false;
- this.popup = false;
- this.enableAdress = false;
- }
+interface Address {
+ streetNumber: string;
+ streetName: string;
+ sublocality: string;
+ city: string;
+ state: string;
+ country: string;
+ postcode: string;
+}
- firstUpdated() {
+@customElement('vehicle-map')
+export class VehicleMap extends LitElement {
+ @property({ attribute: false }) hass!: HomeAssistant;
+ @property({ type: String }) deviceTracker = '';
+ @property({ type: Boolean }) darkMode = false;
+ @property({ type: Boolean }) popup = false;
+
+ @state() private map: L.Map | null = null;
+ @state() private marker: L.Marker | null = null;
+ @state() private lat = 0;
+ @state() private lon = 0;
+ @state() private zoom = 15;
+ @state() private state = '';
+ @state() private address: Partial
= {};
+ @state() private enableAdress = false;
+ @state() private apiKey = '';
+
+ firstUpdated(): void {
this.setEntityAttribute();
}
- updated(changedProperties) {
+ updated(changedProperties: Map): void {
if (changedProperties.has('darkMode')) {
this.updateCSSVariables();
}
}
- setEntityAttribute() {
+ setEntityAttribute(): void {
const deviceTracker = this.hass.states[this.deviceTracker];
if (deviceTracker) {
this.lat = deviceTracker.attributes.latitude;
this.lon = deviceTracker.attributes.longitude;
- this.picture = deviceTracker.attributes.entity_picture;
this.state = deviceTracker.state;
- this.getAddressData(this.lat, this.lon);
- this.darkMode = this.hass.themes.darkMode;
+ this.getAddress(this.lat, this.lon);
}
setTimeout(() => {
this.initMap();
}, 100);
}
- updateCSSVariables() {
+ updateCSSVariables(): void {
if (this.darkMode) {
this.style.setProperty('--map-marker-color', 'var(--accent-color)');
} else {
@@ -70,7 +65,20 @@ export class VehicleMap extends LitElement {
}
}
- static get styles() {
+ async getAddress(lat: number, lon: number): Promise {
+ let address: Partial | null = null;
+ if (this.apiKey !== '') {
+ address = await this.getAddressFromGoggle(lat, lon);
+ } else {
+ address = await this.getAddressFromOpenStreet(lat, lon);
+ }
+ if (address) {
+ this.address = address;
+ this.enableAdress = true;
+ }
+ }
+
+ static get styles(): CSSResultGroup {
return [
mapstyle,
css`
@@ -84,7 +92,6 @@ export class VehicleMap extends LitElement {
width: 100%;
height: 100%;
}
-
#map {
height: 100%;
width: 100%;
@@ -101,7 +108,6 @@ export class VehicleMap extends LitElement {
.marker.dark {
filter: brightness(0.5);
}
-
.dot {
position: absolute;
width: 14px;
@@ -129,7 +135,6 @@ export class VehicleMap extends LitElement {
.marker:hover .dot {
opacity: 1;
}
-
.leaflet-control-container {
display: none;
}
@@ -172,14 +177,17 @@ export class VehicleMap extends LitElement {
];
}
- initMap() {
+ initMap(): void {
const mapOptions = {
dragging: true,
zoomControl: false,
scrollWheelZoom: true,
};
- this.map = L.map(this.shadowRoot.getElementById('map'), mapOptions).setView([this.lat, this.lon], this.zoom);
+ this.map = L.map(this.shadowRoot?.getElementById('map') as HTMLElement, mapOptions).setView(
+ [this.lat, this.lon],
+ this.zoom,
+ );
const tileLayer = this.darkMode ? 'CartoDB.DarkMatter' : 'CartoDB.Positron';
L.tileLayer.provider(tileLayer).addTo(this.map);
@@ -208,7 +216,7 @@ export class VehicleMap extends LitElement {
this.updateMap();
}
- togglePopup() {
+ togglePopup(): void {
const event = new CustomEvent('toggle-map-popup', {
detail: {},
bubbles: true,
@@ -217,13 +225,26 @@ export class VehicleMap extends LitElement {
this.dispatchEvent(event);
}
- updateMap() {
- const offset = this.calculateLatLngOffset(this.map, this.lat, this.lon, this.map.getSize().x / 5, 3);
+ private updateMap(): void {
+ if (!this.map || !this.marker) return;
+ const offset: [number, number] = this.calculateLatLngOffset(
+ this.map,
+ this.lat,
+ this.lon,
+ this.map.getSize().x / 5,
+ 3,
+ );
this.map.setView(offset, this.zoom);
this.marker.setLatLng([this.lat, this.lon]);
}
- calculateLatLngOffset(map, lat, lng, xOffset, yOffset) {
+ private calculateLatLngOffset(
+ map: L.Map,
+ lat: number,
+ lng: number,
+ xOffset: number,
+ yOffset: number,
+ ): [number, number] {
// Convert the lat/lng to a point
const point = map.latLngToContainerPoint([lat, lng]);
// Apply the offset
@@ -233,17 +254,19 @@ export class VehicleMap extends LitElement {
return [newLatLng.lat, newLatLng.lng];
}
- render() {
- return html`
-
-
`;
+ `;
}
- _renderAddress() {
+ private _renderAddress() {
if (!this.enableAdress) return html``;
return html`
@@ -260,9 +283,9 @@ export class VehicleMap extends LitElement {
`;
}
- async getAddressFromOpenStreet(lat, lon) {
- const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=jsonv2`;
-
+ private async getAddressFromOpenStreet(lat: number, lng: number): Promise
| null> {
+ const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=jsonv2`;
+ console.log(url);
try {
const response = await fetch(url);
const data = await response.json();
@@ -284,11 +307,12 @@ export class VehicleMap extends LitElement {
throw new Error('Failed to fetch address');
}
} catch (error) {
+ console.error('Error fetching address:', error);
return null;
}
}
- async getAddressFromGoggle(lat, lng) {
+ private async getAddressFromGoggle(lat: number, lng: number): Promise | null> {
const apiKey = this.apiKey; // Replace with your API key
const url = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=${apiKey}`;
@@ -340,22 +364,4 @@ export class VehicleMap extends LitElement {
return null;
}
}
-
- async getAddressData(lat, lon) {
- let address;
- if (this.apiKey) {
- address = await this.getAddressFromGoggle(lat, lon);
- } else {
- address = await this.getAddressFromOpenStreet(lat, lon);
- }
- if (address) {
- this.address = address;
- this.enableAdress = true;
- this.requestUpdate();
- } else {
- this.enableAdress = false;
- console.log('Could not retrieve address');
- }
- }
}
-customElements.define('vehicle-map', VehicleMap);
diff --git a/src/types.ts b/src/types.ts
index 97512d7..7dc0178 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -26,7 +26,7 @@ export interface VehicleCardConfig extends LovelaceCardConfig {
entity?: string;
device_tracker?: string;
google_api_key?: string;
- images?: string | string[];
+ images?: string[];
show_slides?: boolean;
show_map?: boolean;
show_buttons?: boolean;
diff --git a/src/vehicle-info-card.ts b/src/vehicle-info-card.ts
index 3324970..27585c6 100644
--- a/src/vehicle-info-card.ts
+++ b/src/vehicle-info-card.ts
@@ -29,7 +29,7 @@ import { tapFeedback } from './utils/tap-action.js';
import styles from './css/styles.css';
import { amgBlack, amgWhite } from './utils/imgconst';
import './components/map-card.js';
-import './components/header-slide.js';
+import './components/header-slide';
import './components/eco-chart';
declare global {
interface Window {
@@ -325,9 +325,12 @@ export class VehicleCard extends LitElement {
}
}
- private _renderHeaderSlides(): TemplateResult | void {
- if (!this.config.images || !this.config.show_slides) return;
- return html` `;
+ private _renderHeaderSlides(): TemplateResult {
+ if (!this.config.images || !this.config.show_slides) return html``;
+
+ const images: string[] = this.config.images;
+
+ return html``;
}
private _renderMap(): TemplateResult | void {
@@ -338,13 +341,15 @@ export class VehicleCard extends LitElement {
if (!config.device_tracker && config.show_map) {
return this._showWarning('No device_tracker entity provided.');
}
+ const darkMode = this.isDark();
return html`
`;