Skip to content

Commit

Permalink
Add debug mode
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-panteleev committed Jul 29, 2024
1 parent 53901f5 commit e9936ed
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 62 deletions.
16 changes: 15 additions & 1 deletion src/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,19 @@ export class QRCodeCardEditor extends LitElement implements LovelaceCardEditor {
const config = this._config as WiFiSourceConfig | undefined;
return config?.is_hidden || false;
}

get _is_debug(): boolean {
const config = this._config as QRCodeCardConfig | undefined;
return config?.debug || false;
}

get _entity(): string {
const config = this._config as EntitySourceConfig | undefined;
return config?.entity || ""
}

private _isDisabled(): boolean {
return typeof this._config?.ssid !== "string" || typeof this._config?.password !== "string";
return this._config?.source === SourceType.WIFI && (typeof this._config?.ssid !== "string" || typeof this._config?.password !== "string");
}

private _localize(ts: TranslatableString): string {
Expand Down Expand Up @@ -200,6 +205,15 @@ export class QRCodeCardEditor extends LitElement implements LovelaceCardEditor {
})}
</ha-select>
</div>` : ""}
<div class="values">
<ha-formfield .label=${this._localize("editor.label.is_debug")}>
<ha-switch
.checked=${this._is_debug}
.configValue=${"debug"}
@change=${this._valueChanged}></ha-switch>
</ha-formfield>
</div>
</div>
`;
}
Expand Down
38 changes: 11 additions & 27 deletions src/generator.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,18 @@
import QRCode from "qrcode";

import { DataUrl } from "../types/types";
import { TranslatableError } from "./error";


class QRCodeGenerator {

protected readonly inputString: string;

// TODO: make it configurable
protected readonly quality = {
margin: 1,
width: 500
}

public constructor(inputString: string) {
this.inputString = inputString;
}

public async generate(): Promise<DataUrl> {
try {
return QRCode.toDataURL(this.inputString, this.quality);
}
catch (e: unknown) {
throw new TranslatableError("generation.unknown_error")
}
}
import { DataUrl } from "./types/types";
import { TranslatableError } from "./models/error";

const quality = {
margin: 1,
width: 500
}

export async function generateQR(inputString: string): Promise<DataUrl> {
return await (new QRCodeGenerator(inputString)).generate();
try {
return QRCode.toDataURL(inputString, quality);
}
catch (e: unknown) {
throw new TranslatableError("generation.unknown_error")
}
}
8 changes: 6 additions & 2 deletions src/localize/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"auth_type": "Authentication type (required)",
"ssid": "SSID (required)",
"password": "Password (required)",
"is_hidden": "Hidden SSID"
"is_hidden": "Hidden SSID",
"is_debug": "Debug mode (Show value for QR code)"
},
"title": {
"show_password": "Show password",
Expand All @@ -31,9 +32,12 @@
"nopass": "None"
}
},
"yaml_mode": "Getting SSID and password from entity is not supported in visual editor mode. Please switch to code editor mode."
"yaml_mode": "Configuring SSID and password from entity is not supported in visual editor mode. Please switch to code editor mode."
},
"validation": {
"debug": {
"invalid": "Debug mode must be true or false"
},
"source": {
"missing": "Missing property: source",
"invalid": "Invalid source type"
Expand Down
131 changes: 131 additions & 0 deletions src/models/data-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import {
EntitySourceConfig,
QRCodeCardConfig,
QRCodeGeneratorClass,
TextSourceConfig,
WiFiSourceConfig,
} from "../types/types";
import { isPasswordProtected } from "./authentication-type";
import { SourceType } from "./source-type";
import { HomeAssistant } from "custom-card-helpers";
import { TranslatableError } from "./error";


abstract class QRCodeDataBuilder<T> {

protected readonly config: T;
protected readonly hass: HomeAssistant;

public constructor(hass: HomeAssistant, config: T) {
this.hass = hass;
this.config = config;
}

public getInputString(): string {
try {
return this._getInputString();
}
catch (e: unknown) {
if (e instanceof TranslatableError) {
throw e
} else if (e instanceof Error) {
throw new TranslatableError(["generation.error", "{message}", e.message])
}
throw new TranslatableError("generation.unknown_error")
}
}

protected abstract _getInputString(): string

protected _getValueFromConfig(property: string): string {
let result: string;

const configProperty = this.config[property];
if (configProperty === undefined) {
throw new TranslatableError(`validation.${property}.missing`)
} else if (typeof configProperty === "string") {
result = configProperty;
} else if (configProperty.hasOwnProperty("entity")) {
const entity = this.hass?.states[configProperty.entity]
if (entity === undefined) {
throw new TranslatableError([`validation.${property}.unknown_entity`, "{entity}", configProperty.entity])
}
if (configProperty.attribute !== undefined) {
const attribute_value = entity.attributes[configProperty.attribute];
if (attribute_value === undefined) {
throw new TranslatableError([`validation.${property}.unknown_attribute`, "{attribute}", configProperty.attribute])
}
result = attribute_value.toString();
} else {
const state = entity.state;
if (state === "unavailable") {
throw new TranslatableError([`validation.${property}.unavailable`, "{entity}", configProperty.entity])
}
result = state;
}
} else {
throw new TranslatableError([`validation.${property}.unknown_type`, "{type}", typeof configProperty])
}

return result;
}
}


class TextQRCodeDataBuilder extends QRCodeDataBuilder<TextSourceConfig> {

protected _getInputString(): string {
return this.config.text || "";
}
}


class WiFiQRCodeDataBuilder extends QRCodeDataBuilder<WiFiSourceConfig> {
protected readonly special_chars = ['\\', ';', ',', '"', ':']

protected _escape(plain: string): string {
return this.special_chars.reduce(
(previousValue, currentValue) => {
return previousValue.replace(currentValue, '\\'+currentValue)
},
plain
);
}

protected _getInputString(): string {
const ssid = this._getValueFromConfig("ssid");
let text = `WIFI:T:${this.config.auth_type || ""};S:${this._escape(ssid)};`;

if (isPasswordProtected(this.config.auth_type)) {
const password = this._getValueFromConfig("password");
text += `P:${this._escape(password)};`
}

if (this.config.is_hidden) {
text += "H:true"
}

return text;
}
}

class EntityQRCodeDataBuilder extends QRCodeDataBuilder<EntitySourceConfig> {
protected _getInputString(): string {
return this._getValueFromConfig("entity")
}
}

const configBuilderMapping = new Map<SourceType, QRCodeGeneratorClass<QRCodeDataBuilder<QRCodeCardConfig>>>([
[SourceType.TEXT, TextQRCodeDataBuilder],
[SourceType.WIFI, WiFiQRCodeDataBuilder],
[SourceType.ENTITY, EntityQRCodeDataBuilder]
]);


export function getInputString(hass: HomeAssistant, config: QRCodeCardConfig): string {
const dataBuilderCls = configBuilderMapping.get(config.source);

if (!dataBuilderCls) throw new TranslatableError("validation.source.invalid");

return new dataBuilderCls(hass, config).getInputString();
}
1 change: 1 addition & 0 deletions src/qr-code-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class QRCodeCard extends LitElement {
return html`
<ha-card>
${(this.config?.title ?? "").length > 0 ? html`<h1 class="card-header">${this.config.title}</h1>`: ""}
${(this.config?.debug ?? false) ? html`<p>Input string: ${this.inputString}</p>`: ""}
<div class="qrcode-overlay">
<img class="qrcode" src="${this.dataUrl}">
</div>
Expand Down
1 change: 1 addition & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface EntityConfig {

export interface BaseQRCodeCardConfig extends LovelaceCardConfig {
readonly language?: Language;
readonly debug?: boolean;
readonly title?: string;
readonly source: SourceType;
}
Expand Down
32 changes: 0 additions & 32 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,35 +33,3 @@ export function getWatchedEntities(config: QRCodeCardConfig): string[] {
return [...watchedEntities];
}

export function getValueFromConfig(hass: HomeAssistant, config: QRCodeCardConfig, property: string): string {
let result: string;

const configProperty = config[property];
if (configProperty === undefined) {
throw new TranslatableError(`validation.${property}.missing`)
} else if (typeof configProperty === "string") {
result = configProperty;
} else if (configProperty.hasOwnProperty("entity")) {
const entity = hass?.states[configProperty.entity]
if (entity === undefined) {
throw new TranslatableError([`validation.${property}.unknown_entity`, "{entity}", configProperty.entity])
}
if (configProperty.attribute !== undefined) {
const attribute_value = entity.attributes[configProperty.attribute];
if (attribute_value === undefined) {
throw new TranslatableError([`validation.${property}.unknown_attribute`, "{attribute}", configProperty.attribute])
}
result = attribute_value.toString();
} else {
const state = entity.state;
if (state === "unavailable") {
throw new TranslatableError([`validation.${property}.unavailable`, "{entity}", configProperty.entity])
}
result = state;
}
} else {
throw new TranslatableError([`validation.${property}.unknown_type`, "{type}", typeof configProperty])
}

return result;
}
13 changes: 13 additions & 0 deletions src/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ abstract class Validator<T> {

}

class DebugModeValidator extends Validator<QRCodeCardConfig> {
protected _validate(): string[] {
const errors: string[] = [];

if (this.config.debug !== undefined && typeof this.config.debug !== "boolean") {
errors.push("validation.debug.invalid");
}

return errors;
}
}

class SourceValidator extends Validator<QRCodeCardConfig> {
protected _validate(): string[] {
const errors: string[] = [];
Expand Down Expand Up @@ -108,6 +120,7 @@ export function validateConfig(config: QRCodeCardConfig): string[] {
const errors: TranslatableString[] = [];

new SourceValidator(config).validate().forEach(e => errors.push(e));
new DebugModeValidator(config).validate().forEach(e => errors.push(e));

if (errors.length == 0) {
const validatorCls = validatorMap.get(config.source);
Expand Down

0 comments on commit e9936ed

Please sign in to comment.