diff --git a/lunar-config-editor.gif b/lunar-config-editor.gif deleted file mode 100644 index 73a1b5e..0000000 Binary files a/lunar-config-editor.gif and /dev/null differ diff --git a/package.json b/package.json index 8685baf..012ed29 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "@mdi/js": "^7.4.47", "@noim/suncalc3": "^2.0.5", + "@vibrant/color": "^3.2.1-alpha.1", "chart.js": "^4.4.5", "custom-card-helpers": "^1.7.2", "lit": "^3.1.4", diff --git a/rollup.config.js b/rollup.config.js index 1bdc6e9..c2178c3 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -88,7 +88,6 @@ export default [ banner: custombanner, }, ], - external: ['Vibrant'], plugins: [...plugins], moduleContext: (id) => { const thisAsWindowForModules = [ diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000..74d13bb --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,6 @@ +export * from './moon-calendar-popup'; +export * from './moon-base-data'; +export * from './moon-horizon-dynamic'; +export * from './moon-horizon-chart'; +export * from './moon-star-field'; +export * from './moon-editor-search'; diff --git a/src/components/moon-data.ts b/src/components/moon-base-data.ts similarity index 99% rename from src/components/moon-data.ts rename to src/components/moon-base-data.ts index a5f7182..d754f51 100644 --- a/src/components/moon-data.ts +++ b/src/components/moon-base-data.ts @@ -55,7 +55,7 @@ export class LunarBaseData extends LitElement { } .moon-phase-name { font-size: 1.5rem; - padding-block: 0.5rem; + padding-block: 1rem; } `, mainStyles, diff --git a/src/components/moon-calendar-popup.ts b/src/components/moon-calendar-popup.ts index ec81d39..72858e1 100644 --- a/src/components/moon-calendar-popup.ts +++ b/src/components/moon-calendar-popup.ts @@ -8,8 +8,8 @@ import { Moon } from '../utils/moon'; // styles import styles from '../css/style.css'; -@customElement('moon-calendar-popup') -export class MoonCalendarPopup extends LitElement { +@customElement('lunar-calendar-popup') +export class LunarCalendarPopup extends LitElement { @property({ attribute: false }) card!: LunarPhaseCard; @property({ attribute: false }) moon!: Moon; @state() viewDate = DateTime.local().startOf('month'); @@ -70,7 +70,7 @@ export class MoonCalendarPopup extends LitElement { #calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); - grid-template-rows: repeat(7, 1fr); + /* grid-template-rows: repeat(7, 1fr); */ padding: 0.5em; cursor: default; gap: 2px 4px; @@ -240,6 +240,6 @@ export class MoonCalendarPopup extends LitElement { } declare global { interface HTMLElementTagNameMap { - 'moon-calendar-popup': MoonCalendarPopup; + 'lunar-calendar-popup': LunarCalendarPopup; } } diff --git a/src/components/moon-horizon.ts b/src/components/moon-horizon-chart.ts similarity index 99% rename from src/components/moon-horizon.ts rename to src/components/moon-horizon-chart.ts index 500d383..30b4de8 100644 --- a/src/components/moon-horizon.ts +++ b/src/components/moon-horizon-chart.ts @@ -26,8 +26,8 @@ import { MOON_RISE_ICON, MOON_SET_ICON } from '../utils/moon-pic'; const HOVER_TIMEOUT = 200; -@customElement('moon-horizon') -export class MoonHorizon extends LitElement { +@customElement('lunar-horizon-chart') +export class LunarHorizonChart extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) moon!: Moon; @property({ attribute: false }) card!: LunarPhaseCard; @@ -787,6 +787,6 @@ export class MoonHorizon extends LitElement { declare global { interface HTMLElementTagNameMap { - 'moon-horizon': MoonHorizon; + 'lunar-horizon-chart': LunarHorizonChart; } } diff --git a/src/components/moon-horizon-dynamic.ts b/src/components/moon-horizon-dynamic.ts index 625f89b..8a9d2d8 100644 --- a/src/components/moon-horizon-dynamic.ts +++ b/src/components/moon-horizon-dynamic.ts @@ -1,35 +1,38 @@ // Lit import { LitElement, html, CSSResultGroup, TemplateResult, css, PropertyValues } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; - +import tinycolor from 'tinycolor2'; // Custom Card helpers -import { FrontendLocaleData, HomeAssistant, formatDateShort, formatTime } from 'custom-card-helpers'; +import { FrontendLocaleData, formatDateShort, formatTime } from 'custom-card-helpers'; // Chart.js import { Chart, ChartData, ChartOptions, Plugin, ScriptableLineSegmentContext } from 'chart.js/auto'; // DateTime import { DateTime } from 'luxon'; // Local imports + import { CHART_COLOR, CHART_DATA } from '../const'; import { LunarPhaseCard } from '../lunar-phase-card'; -import extractColorData from '../utils/extractColorData.js'; +import { FILL_COLORS, HA as HomeAssistant } from '../types'; +import extract_color from '../utils/extract_color'; import { hexToRgba } from '../utils/helpers'; import { Moon } from '../utils/moon'; // Styles import styles from '../css/style.css'; -@customElement('moon-horizon-dynamic') -export class MoonHorizonDynamic extends LitElement { +@customElement('lunar-horizon-dynamic') +export class LunarHorizonDynamic extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) moon!: Moon; @property({ attribute: false }) card!: LunarPhaseCard; @property({ type: Number }) cardWidth: number = 0; - @state() public _midnightColor: Record = {}; + @state() public fillColors!: FILL_COLORS; @state() dynamicChart!: Chart; + @state() tinyColor = tinycolor; protected async firstUpdated(): Promise { - await this.extractColorData(); + this.fillColors = await this.extractColorData(); await new Promise((resolve) => setTimeout(resolve, 0)); this.initChart(); } @@ -201,7 +204,8 @@ export class MoonHorizonDynamic extends LitElement { tension: 0.2, borderWidth: 1, }; - const { PRIMARY_TEXT, SECONDARY_TEXT, fillBelowColor, fillColor, fillBelowLineColor } = this.CSS_COLOR; + const { PRIMARY_TEXT, SECONDARY_TEXT, fillBelowColor, fillBelowLineColor } = this.CSS_COLOR; + const fillAbove = this.fillColors.fillAbove; const BORDER_COLORS = { BOLD: isBackground ? CHART_COLOR.MOON_LINE_BOLD : PRIMARY_TEXT, LIGHT: isBackground ? fillBelowLineColor : SECONDARY_TEXT, @@ -214,7 +218,7 @@ export class MoonHorizonDynamic extends LitElement { data: moonData, fill: { target: { value: 0 }, // Fill area above 0° altitude - above: fillColor, + above: fillAbove, below: fillBelowColor, }, segment: { @@ -357,14 +361,14 @@ export class MoonHorizonDynamic extends LitElement { } = chart; const xLabel = chart.scales.x.getPixelForValue(index); const yLabel = chart.scales.y.getPixelForValue(chartData[index].moon); - const lineColor = hexToRgba(CHART_COLOR.STROKE_LINE, 0.7); + // const lineColor = hexToRgba(CHART_COLOR.STROKE_LINE, 0.7); ctx.font = '12px Arial'; const width = ctx.measureText(nowText).width; // Draw the dashed line and label for the current time ctx.save(); ctx.beginPath(); - ctx.strokeStyle = lineColor; + ctx.strokeStyle = CHART_COLOR.STROKE_LINE; ctx.setLineDash([2, 4]); ctx.lineWidth = 1; ctx.moveTo(xLabel, yLabel); @@ -404,7 +408,7 @@ export class MoonHorizonDynamic extends LitElement { private _midnightPosition(): Plugin { const { SECONDARY_TEXT } = this.CSS_COLOR; - const { todayFill, nextDayFill } = this._midnightColor; + const { today: todayFill, nextDay: nextDayFill } = this.fillColors; const fontSize = '12px Arial'; const { chartData } = this.todayData; const timeLabels = chartData.map((data) => data.timeLabel); @@ -508,8 +512,23 @@ export class MoonHorizonDynamic extends LitElement { }, }; } + + private _getReadAbleColor() { + const todayColor = this.fillColors.today; + + const allColors: string[] = []; + for (const color in tinycolor.names) { + allColors.push(color); + } + const readAbleColors = tinycolor.mostReadable(todayColor, allColors); + const baseColor = tinycolor(readAbleColors); + const lightColor = baseColor.clone().setAlpha(0.2).toString(); + + return { baseColor: baseColor.setAlpha(0.4).toString(), lightColor }; + } + private _timesMarkersPlugin(): Plugin { - const { SECONDARY_TEXT, PRIMARY_TEXT } = this.CSS_COLOR; + const labelColors = this._getReadAbleColor(); const fontSize = { primary: '0.9rem Arial', secondary: '0.8rem Arial', @@ -570,7 +589,7 @@ export class MoonHorizonDynamic extends LitElement { times.forEach((time) => { const xPos = x.getPixelForValue(time.index); const yPos = y.getPixelForValue(0); - const color = isPast(time.originalTime) ? hexToRgba(SECONDARY_TEXT, 0.5) : hexToRgba(PRIMARY_TEXT, 0.8); + const color = isPast(time.originalTime) ? labelColors.lightColor : labelColors.baseColor; const relativeTime = isPast(time.originalTime) ? '' : calculateDuration(time.originalTime); drawPoint(ctx, xPos, yPos, color, time.time, lineHeight, relativeTime); }); @@ -590,44 +609,39 @@ export class MoonHorizonDynamic extends LitElement { return { id: 'expandChartArea', beforeRender: (chart: Chart) => { - chart.chartArea.right = chart.width; - chart.chartArea.bottom = chart.height; + chart.chartArea.right = this.cardWidth; + chart.chartArea.bottom = this.cardHeight; }, afterUpdate: (chart: Chart) => { - chart.chartArea.right = chart.width; - chart.chartArea.bottom = chart.height; + chart.chartArea.right = this.cardWidth; + chart.chartArea.bottom = this.cardHeight; }, }; }; - async extractColorData(): Promise { + async extractColorData(): Promise { + const defaultColors = { + today: CHART_COLOR.TODAY_FILL, + nextDay: CHART_COLOR.NEXTDAY_FILL, + fillAbove: CHART_COLOR.FILL_ABOVE, + }; const custom_background = this.card.config?.custom_background; if (!custom_background || this.card.config.show_background === false) { - this._midnightColor = { - todayFill: CHART_COLOR.TODAY_FILL, - nextDayFill: CHART_COLOR.NEXTDAY_FILL, - }; - return; + return defaultColors; } try { - const data = await extractColorData(custom_background); - this._midnightColor = { - todayFill: data.todayFillColor, - nextDayFill: data.nextDayFillColor, - }; + const data = await extract_color(custom_background); + return data; } catch (error) { - this._midnightColor = { - todayFill: CHART_COLOR.TODAY_FILL, - nextDayFill: CHART_COLOR.NEXTDAY_FILL, - }; + return defaultColors; } } } declare global { interface HTMLElementTagNameMap { - 'moon-horizon-dynamic': MoonHorizonDynamic; + 'lunar-horizon-dynamic': LunarHorizonDynamic; } } diff --git a/src/components/moon-star-field.ts b/src/components/moon-star-field.ts index 4a81e2a..2a7e0ac 100644 --- a/src/components/moon-star-field.ts +++ b/src/components/moon-star-field.ts @@ -4,7 +4,7 @@ import { customElement, property, state } from 'lit/decorators.js'; import { LunarPhaseCard } from '../lunar-phase-card'; @customElement('moon-star-field') -export class MoonStarField extends LitElement { +export class LunarStarField extends LitElement { @property({ attribute: false }) _card!: LunarPhaseCard; @state() public count: number = 30; diff --git a/src/const.ts b/src/const.ts index 154d782..75418ac 100644 --- a/src/const.ts +++ b/src/const.ts @@ -81,6 +81,7 @@ export const enum CHART_DATA { export const enum CHART_COLOR { TODAY_FILL = '#47546b', // Original NEXTDAY_FILL = '#3d4b63', // Original + FILL_ABOVE = '#47546b33', STROKE_LINE = '#7a8eaa', SECONDARY_TEXT = '#9b9b9b', PRIMARY_TEXT = '#e1e1e1', diff --git a/src/css/style.css b/src/css/style.css index ad1b7e0..9ae03d2 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -149,7 +149,7 @@ ha-card>.loading[hidden] { ha-card.--background h1, ha-card.--background .moon-phase-name { color: var(--lunar-card-header-font-color, #e1e1e1); - text-shadow: 2px 2px 4px var(--card-background-color); + /* text-shadow: 2px 2px 4px var(--card-background-color); */ } .--default-header h1 { diff --git a/src/lunar-phase-card.ts b/src/lunar-phase-card.ts index 3c0cf39..94d6e8a 100644 --- a/src/lunar-phase-card.ts +++ b/src/lunar-phase-card.ts @@ -1,11 +1,11 @@ -import { LovelaceCardEditor, formatDate, FrontendLocaleData, TimeFormat, HomeAssistant } from 'custom-card-helpers'; +import { LovelaceCardEditor, formatDate, FrontendLocaleData, TimeFormat } from 'custom-card-helpers'; import { LitElement, html, TemplateResult, PropertyValues, CSSResultGroup, nothing } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { choose } from 'lit/directives/choose.js'; import { classMap } from 'lit/directives/class-map.js'; import { styleMap } from 'lit/directives/style-map.js'; // Local types -import { LunarPhaseCardConfig, defaultConfig } from './types'; +import { HA as HomeAssistant, LunarPhaseCardConfig, defaultConfig } from './types'; // Helpers import { BLUE_BG, PageType, MoonState, ICON } from './const'; @@ -15,17 +15,12 @@ import { _handleOverflow, _setEventListeners, getDefaultConfig } from './utils/h import { isEditorMode } from './utils/loader'; // components -import { LunarBaseData } from './components/moon-data'; -import { MoonHorizon } from './components/moon-horizon'; -import { MoonHorizonDynamic } from './components/moon-horizon-dynamic'; -import { MoonStarField } from './components/moon-star-field'; +import { LunarBaseData } from './components/moon-base-data'; +import { LunarHorizonChart } from './components/moon-horizon-chart'; +import { LunarHorizonDynamic } from './components/moon-horizon-dynamic'; +import { LunarStarField } from './components/moon-star-field'; import { Moon } from './utils/moon'; -import './components/moon-data'; -import './components/moon-horizon'; -import './components/moon-horizon-dynamic'; -import './components/moon-star-field'; -import './components/moon-calendar-popup'; - +import './components'; // styles import style from './css/style.css'; @@ -44,11 +39,11 @@ export class LunarPhaseCard extends LitElement { this._hass = hass; } - @state() _hass!: HomeAssistant; + @state() private _hass!: HomeAssistant; @property({ attribute: false }) config!: LunarPhaseCardConfig; - @state() moon!: Moon; - @state() public _activeCard: PageType | null = null; - @state() public selectedDate: Date | undefined; + @property({ attribute: false }) moon!: Moon; + @state() _activeCard: PageType | null = null; + @state() selectedDate: Date | undefined; @state() _calendarPopup: boolean = false; @state() _calendarInfo: boolean = false; @@ -59,16 +54,19 @@ export class LunarPhaseCard extends LitElement { @state() private _state: MoonState = MoonState.READY; @state() private _refreshInterval: number | undefined; - @state() _activeEditorPage?: PageType; - @state() _resizeObserver: ResizeObserver | null = null; - @state() _resizeEntries: ResizeObserverEntry[] = []; - @state() public _cardWidth!: number; - @state() public _cardHeight!: number; + @state() private _resizeObserver: ResizeObserver | null = null; + @state() private _resizeEntries: ResizeObserverEntry[] = []; + @state() private _cardWidth: number = 0; + @state() private _cardHeight: number = 0; @query('lunar-base-data') _data!: LunarBaseData; - @query('moon-horizon') _moonHorizon!: MoonHorizon; - @query('moon-horizon-dynamic') _moonHorizonDynamic!: MoonHorizonDynamic; - @query('moon-star-field') _starField!: MoonStarField; + @query('lunar-horizon-chart') _moonHorizon!: LunarHorizonChart; + @query('lunar-horizon-dynamic') _moonHorizonDynamic!: LunarHorizonDynamic; + @query('moon-star-field') _starField!: LunarStarField; + + constructor() { + super(); + } // https://lit.dev/docs/components/styles/ static get styles(): CSSResultGroup { @@ -147,16 +145,16 @@ export class LunarPhaseCard extends LitElement { } private measureCard(): void { - const card = this.shadowRoot?.querySelector('ha-card') as HTMLElement; - if (card) { - this._cardWidth = card.clientWidth; - this._cardHeight = card.clientHeight; + if (this._resizeEntries.length > 0) { + const entry = this._resizeEntries[0]; + this._cardWidth = entry.contentRect.width; + this._cardHeight = entry.contentRect.height; } } private _handleEditorEvent(event: CustomEvent) { event.stopPropagation(); - if (!isEditorMode(this)) { + if (!isEditorMode(this) || this._defaultCard === PageType.HORIZON) { return; } this._handleFirstRender(); @@ -297,11 +295,12 @@ export class LunarPhaseCard extends LitElement { if (!this._connected) { return; } - if (this._state !== MoonState.LOADING) { + console.log('Refreshing data'); this._state = MoonState.LOADING; setTimeout(() => { this._state = MoonState.READY; + console.log('Data refreshed'); }, LOADING_TIMEOUT); } } @@ -340,7 +339,7 @@ export class LunarPhaseCard extends LitElement { [PageType.HORIZON, () => this.renderHorizon()], ])} - + `; } @@ -500,7 +499,7 @@ export class LunarPhaseCard extends LitElement {
${this.renderMoonData()}
- +
`; @@ -519,18 +518,18 @@ export class LunarPhaseCard extends LitElement { private renderHorizon(): TemplateResult { const graphType = this.config.graph_config?.graph_type; - const defaultGraph = html``; - const dynamicGraph = html``; + const dynamicGraph = html``; + >`; return html` ${choose( @@ -546,6 +545,7 @@ export class LunarPhaseCard extends LitElement { private updateDate(action?: 'next' | 'prev') { const date = new Date(this._date); + date.setHours(0, 0, 0, 0); if (action === 'next') { date.setDate(date.getDate() + 1); } else if (action === 'prev') { @@ -640,8 +640,8 @@ export class LunarPhaseCard extends LitElement { } } -(window as any).customCards = (window as any).customCards || []; -(window as any).customCards.push({ +window.customCards = window.customCards || []; +window.customCards.push({ type: 'lunar-phase-card', name: 'Lunar Phase Card', preview: true, @@ -653,6 +653,17 @@ declare global { LunarCard: LunarPhaseCard; Moon: Moon; } + interface Window { + customCards: CustomCard[]; + } + + interface CustomCard { + type: string; + name: string; + preview: boolean; + description: string; + } + interface HTMLElementTagNameMap { 'lunar-phase-card': LunarPhaseCard; } diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 7b7a477..0000000 --- a/src/types.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { IMoonData, IMoonIllumination } from '@noim/suncalc3'; -// Cutom card helpers: -import { LovelaceCardConfig } from 'custom-card-helpers'; - -import { PageType } from './const'; - -export type FontSizeOptions = 'auto' | 'small' | 'medium' | 'large' | 'x-large' | 'xx-large'; -export type FontTextTransformOptions = 'none' | 'capitalize' | 'uppercase' | 'lowercase'; - -export interface FontCustomStyles { - header_font_size: FontSizeOptions; - header_font_style: FontTextTransformOptions; - header_font_color: string; - label_font_size: FontSizeOptions; - label_font_style: FontTextTransformOptions; - label_font_color: string; - hide_label: boolean; -} - -interface HorizonGraphConfig { - graph_type?: 'default' | 'dynamic'; - y_ticks?: boolean; - x_ticks?: boolean; - show_time?: boolean; - show_current?: boolean; - show_legend?: boolean; - show_highest?: boolean; - y_ticks_position?: 'left' | 'right'; - y_ticks_step_size?: number; - legend_position?: 'top' | 'bottom'; - legend_align?: 'start' | 'center' | 'end'; - time_step_size?: number; -} - -export interface LunarPhaseCardConfig extends LovelaceCardConfig { - type: string; - entity?: string; - use_default?: boolean; - use_custom?: boolean; - use_entity?: boolean; - show_background?: boolean; - compact_view?: boolean; - '12hr_format'?: boolean; - mile_unit?: boolean; - number_decimals?: number; - hide_header?: boolean; - default_card?: PageType.BASE | PageType.CALENDAR | PageType.HORIZON; - selected_language: string; - moon_position?: 'left' | 'right'; - southern_hemisphere?: boolean; - latitude: number; - longitude: number; - location?: LocationAddress; - font_customize: FontCustomStyles; - graph_config?: HorizonGraphConfig; - cardId?: string; -} - -export const defaultConfig: Partial = { - type: 'custom:lunar-phase-card', - entity: '', - use_default: true, - use_custom: false, - use_entity: false, - show_background: true, - selected_language: 'en', - compact_view: true, - '12hr_format': false, - mile_unit: false, - hide_header: false, - default_card: PageType.BASE, - moon_position: 'left', - southern_hemisphere: false, - number_decimals: 2, - graph_config: { - graph_type: 'default', - y_ticks: false, - x_ticks: true, - show_time: true, - show_current: true, - show_legend: true, - show_highest: true, - y_ticks_position: 'left', - y_ticks_step_size: 30, - legend_position: 'top', - legend_align: 'center', - time_step_size: 30, - }, - font_customize: { - header_font_size: 'x-large', - header_font_style: 'capitalize', - header_font_color: '', - label_font_size: 'auto', - label_font_style: 'none', - label_font_color: '', - hide_label: false, - }, -}; - -export type Location = { - latitude: number; - longitude: number; -}; - -export type MoonDataItem = { - label: string; - value: string; - secondValue?: string; -}; -export type MoonImage = { - moonPic: string; - rotateDeg: number; -}; - -export interface MoonData { - moonFraction: MoonDataItem; - moonAge: MoonDataItem; - moonRise: MoonDataItem; - moonSet: MoonDataItem; - moonHighest?: MoonDataItem; - distance: MoonDataItem; - azimuthDegress: MoonDataItem; - altitudeDegrees: MoonDataItem; - nextFullMoon: MoonDataItem; - nextNewMoon: MoonDataItem; - direction: MoonDataItem; - position: MoonDataItem; -} - -export type ChartColors = { - primaryTextColor: string; - secondaryTextColor: string; - fillColor: string; - fillBelowColor: string; - fillBelowLineColor: string; - [key: string]: string; -}; - -export type LocationAddress = { - country: string; - city: string; -}; - -export type SearchResults = { - addresstype: string; - display_name: string; - name: string; - lat: number; - lon: number; -}; - -export type DynamicChartData = { - chartData: { - timeLabel: number; - moon: number; - }[]; - times: { - moon: number[]; - }; - moonIllumination: IMoonIllumination; - moonData: IMoonData; -}; diff --git a/src/types/chart-config.ts b/src/types/chart-config.ts new file mode 100644 index 0000000..2df23ad --- /dev/null +++ b/src/types/chart-config.ts @@ -0,0 +1,54 @@ +import { IMoonData, IMoonIllumination } from '@noim/suncalc3'; + +export type MoonDataItem = { + label: string; + value: string; + secondValue?: string; +}; +export type MoonImage = { + moonPic: string; + rotateDeg: number; +}; + +export interface MoonData { + moonFraction: MoonDataItem; + moonAge: MoonDataItem; + moonRise: MoonDataItem; + moonSet: MoonDataItem; + moonHighest?: MoonDataItem; + distance: MoonDataItem; + azimuthDegress: MoonDataItem; + altitudeDegrees: MoonDataItem; + nextFullMoon: MoonDataItem; + nextNewMoon: MoonDataItem; + direction: MoonDataItem; + position: MoonDataItem; +} + +export type ChartColors = { + primaryTextColor: string; + secondaryTextColor: string; + fillColor: string; + fillBelowColor: string; + fillBelowLineColor: string; + [key: string]: string; +}; + +export type DynamicChartData = { + chartData: { + timeLabel: number; + moon: number; + }[]; + times: { + moon: number[]; + }; + moonIllumination: IMoonIllumination; + moonData: IMoonData; +}; + +export interface FILL_COLORS { + today: string; + nextDay: string; + fillAbove: string; + [key: string]: string; +} diff --git a/src/types/config.ts b/src/types/config.ts new file mode 100644 index 0000000..0824029 --- /dev/null +++ b/src/types/config.ts @@ -0,0 +1,69 @@ +// Cutom card helpers: +import { LovelaceCardConfig } from 'custom-card-helpers'; + +import { PageType } from '../const'; + +export type FontSizeOptions = 'auto' | 'small' | 'medium' | 'large' | 'x-large' | 'xx-large'; +export type FontTextTransformOptions = 'none' | 'capitalize' | 'uppercase' | 'lowercase'; + +export interface FontCustomStyles { + header_font_size: FontSizeOptions; + header_font_style: FontTextTransformOptions; + header_font_color: string; + label_font_size: FontSizeOptions; + label_font_style: FontTextTransformOptions; + label_font_color: string; + hide_label: boolean; +} + +interface HorizonGraphConfig { + graph_type?: 'default' | 'dynamic'; + y_ticks?: boolean; + x_ticks?: boolean; + show_time?: boolean; + show_current?: boolean; + show_legend?: boolean; + show_highest?: boolean; + y_ticks_position?: 'left' | 'right'; + y_ticks_step_size?: number; + legend_position?: 'top' | 'bottom'; + legend_align?: 'start' | 'center' | 'end'; + time_step_size?: number; +} + +export interface LunarPhaseCardConfig extends LovelaceCardConfig { + type: string; + entity?: string; + use_default?: boolean; + use_custom?: boolean; + use_entity?: boolean; + show_background?: boolean; + compact_view?: boolean; + '12hr_format'?: boolean; + mile_unit?: boolean; + number_decimals?: number; + hide_header?: boolean; + default_card?: PageType.BASE | PageType.CALENDAR | PageType.HORIZON; + selected_language: string; + moon_position?: 'left' | 'right'; + southern_hemisphere?: boolean; + latitude: number; + longitude: number; + location?: LocationAddress; + font_customize: FontCustomStyles; + graph_config?: HorizonGraphConfig; + cardId?: string; +} + +export type LocationAddress = { + country: string; + city: string; +}; + +export type SearchResults = { + addresstype: string; + display_name: string; + name: string; + lat: number; + lon: number; +}; diff --git a/src/declarations.d.ts b/src/types/declarations.d.ts similarity index 100% rename from src/declarations.d.ts rename to src/types/declarations.d.ts diff --git a/src/types/default-config.ts b/src/types/default-config.ts new file mode 100644 index 0000000..ad12453 --- /dev/null +++ b/src/types/default-config.ts @@ -0,0 +1,43 @@ +import { PageType } from '../const'; +import { LunarPhaseCardConfig } from './config'; + +export const defaultConfig: Partial = { + type: 'custom:lunar-phase-card', + entity: '', + use_default: true, + use_custom: false, + use_entity: false, + show_background: true, + selected_language: 'en', + compact_view: true, + '12hr_format': false, + mile_unit: false, + hide_header: false, + default_card: PageType.BASE, + moon_position: 'left', + southern_hemisphere: false, + number_decimals: 2, + graph_config: { + graph_type: 'default', + y_ticks: false, + x_ticks: true, + show_time: true, + show_current: true, + show_legend: true, + show_highest: true, + y_ticks_position: 'left', + y_ticks_step_size: 30, + legend_position: 'top', + legend_align: 'center', + time_step_size: 30, + }, + font_customize: { + header_font_size: 'x-large', + header_font_style: 'capitalize', + header_font_color: '', + label_font_size: 'auto', + label_font_style: 'none', + label_font_color: '', + hide_label: false, + }, +}; diff --git a/src/types/ha.ts b/src/types/ha.ts new file mode 100644 index 0000000..357daae --- /dev/null +++ b/src/types/ha.ts @@ -0,0 +1,32 @@ +import { HomeAssistant } from 'custom-card-helpers'; + +export interface ThemeVars { + // Incomplete + 'primary-color': string; + 'text-primary-color': string; + 'accent-color': string; + [key: string]: string; +} + +export type Theme = ThemeVars & { + modes?: { + light?: ThemeVars; + dark?: ThemeVars; + }; +}; + +export interface Themes { + default_theme: string; + default_dark_theme: string | null; + themes: Record; + // Currently effective dark mode. Will never be undefined. If user selected "auto" + // in theme picker, this property will still contain either true or false based on + // what has been determined via system preferences and support from the selected theme. + darkMode: boolean; + // Currently globally active theme name + theme: string; +} + +export type HA = HomeAssistant & { + themes: Themes; +}; diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..93ad19a --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,4 @@ +export * from './chart-config'; +export * from './ha'; +export * from './config'; +export * from './default-config'; diff --git a/src/utils/extractColorData.js b/src/utils/extractColorData.js deleted file mode 100644 index 83fee40..0000000 --- a/src/utils/extractColorData.js +++ /dev/null @@ -1,24 +0,0 @@ -import Vibrant from 'node-vibrant/dist/vibrant'; -import tinycolor from 'tinycolor2'; - -const extractColorData = (palette) => { - const colors = {}; - for (const [key, value] of Object.entries(palette)) { - // console.log(key, value.getHex()); - if (key === 'Vibrant') { - const todayFillColor = value.getHex(); - const nextDayFillColor = tinycolor(todayFillColor).darken(10).toHexString(); - colors.todayFillColor = todayFillColor; - colors.nextDayFillColor = nextDayFillColor; - } - } - // console.log(colors); - return colors; -}; - -export default async (picture) => { - const vibrant = new Vibrant(picture, { colorCount: 250 }); - const palette = await vibrant.getPalette(); - const colorData = extractColorData(palette); - return colorData; -}; diff --git a/src/utils/extract_color.ts b/src/utils/extract_color.ts new file mode 100644 index 0000000..04b22c9 --- /dev/null +++ b/src/utils/extract_color.ts @@ -0,0 +1,27 @@ +import { Palette } from '@vibrant/color'; +import Vibrant from 'node-vibrant/dist/vibrant'; +import tinycolor from 'tinycolor2'; + +import { FILL_COLORS } from '../types'; + +const extractColorData = (palette: Palette): FILL_COLORS => { + const colors = {} as FILL_COLORS; + for (const [key, value] of Object.entries(palette)) { + if (value && key === 'Vibrant') { + const todayFillColor = value.hex; + const fillAboveColor = tinycolor(todayFillColor).setAlpha(0.2).toHex8String(); + const nextDayFillColor = tinycolor(todayFillColor).darken(10).toHexString(); + colors.today = todayFillColor; + colors.nextDay = nextDayFillColor; + colors.fillAbove = fillAboveColor; + } + } + return colors; +}; + +export default async (picture: string): Promise => { + const vibrant = new Vibrant(picture, { colorCount: 250 }); + const palette = await vibrant.getPalette(); + const colorData = extractColorData(palette); + return colorData; +}; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 0990236..026b07c 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -241,7 +241,6 @@ export function _setEventListeners(card: LunarPhaseCard): void { const pressDown = (e: TouchEvent | MouseEvent) => { const target = e.target as HTMLElement; - console.log('pressDown', target.tagName); if (target.tagName === 'LUNAR-BASE-DATA') return; e.stopImmediatePropagation(); if (e instanceof TouchEvent) { diff --git a/src/utils/moon.ts b/src/utils/moon.ts index c4ad8cc..a9d292b 100644 --- a/src/utils/moon.ts +++ b/src/utils/moon.ts @@ -4,10 +4,15 @@ import { DateTime, WeekdayNumbers } from 'luxon'; import { CHART_DATA } from '../const'; import { localize } from '../localize/localize'; -import { LunarPhaseCardConfig, MoonData, MoonDataItem, MoonImage, Location, DynamicChartData } from '../types'; +import { LunarPhaseCardConfig, MoonData, MoonDataItem, MoonImage, DynamicChartData } from '../types'; import { MOON_IMAGES } from '../utils/moon-pic'; import { convertKmToMiles, compareTime } from './helpers'; +type Location = { + latitude: number; + longitude: number; +}; + // Moon class export class Moon { readonly _date: Date; @@ -155,7 +160,7 @@ export class Moon { moonHighest: createMoonTime('moonHigh', new Date(highest as Date)), nextFullMoon: createItem('fullMoon', shortTime(fullMoon.value)), nextNewMoon: createItem('newMoon', shortTime(newMoon.value)), - direction: createItem('direction', formatted.azimuth, '°', cardinal), + direction: createItem('azimuth', formatted.azimuth, '°', cardinal), position: createItem('position', localize(`card.${altitudeDegrees > 0 ? 'overHorizon' : 'underHorizon'}`)), }; } diff --git a/tsconfig.json b/tsconfig.json index 62d6333..e28cb1e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,9 +2,9 @@ "compilerOptions": { "target": "es2017", "module": "esnext", - "allowSyntheticDefaultImports": true, "moduleResolution": "node", "lib": ["es2017", "dom", "dom.iterable"], + "allowSyntheticDefaultImports": true, "noEmit": true, "noUnusedParameters": true, "noImplicitReturns": true, @@ -14,11 +14,10 @@ "skipLibCheck": true, "resolveJsonModule": true, "experimentalDecorators": true, - "outDir": "./dist", - "rootDir": "./src", - "typeRoots": ["./node_modules/@types", "./src/declarations.d.ts"], - "sourceMap": true, - "plugins": [{ "name": "ts-lit-plugin" }] + "outDir": "dist", + "rootDir": ".", + "sourceMap": true }, - "include": ["src/**/*"] + "include": ["**/*.ts"], + "exclude": ["node_modules"] }