Skip to content

Commit

Permalink
feat(deviceImage): allow to pre download device images
Browse files Browse the repository at this point in the history
  • Loading branch information
nurikk committed Nov 20, 2023
1 parent 0e7d9ae commit db81940
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/components/device-image/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type DeviceImageProps = {
};

type ImageGeneratorFn = (device: Device) => string | undefined;
const getZ2mDeviceImage = (device: Device): string =>
export const getZ2mDeviceImage = (device: Device): string =>
`https://www.zigbee2mqtt.io/images/devices/${sanitizeZ2MDeviceName(device?.definition?.model)}.jpg`;
const getConverterDeviceImage = (device: Device): string | undefined => device.definition?.icon;

Expand Down
88 changes: 88 additions & 0 deletions src/components/settings-page/image-localiser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { Component, useEffect, useState } from 'react';

import { WithTranslation, withTranslation } from "react-i18next";
import Button from '../button';
import { Device, IEEEEAddress } from '../../types';
import { WithDevices } from '../../store';
import { DeviceApi } from '../../actions/DeviceApi';
import { getZ2mDeviceImage } from '../device-image';

type LocaliserState = 'none' | 'start' | 'inprogress' | 'done';

type Props = WithTranslation<'setting'> & WithDevices & Pick<DeviceApi, 'setDeviceOptions'>;


type LStatus = "init" | "error" | "done";

function blobToBase64(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result as string);
reader.onerror = (err) => reject(err);
reader.readAsDataURL(blob);
});
}

async function downloadImage(imageSrc: string): Promise<string> {
const image = await fetch(imageSrc);
if (image.ok) {
const blob = await image.blob()
return blobToBase64(blob);
} else {
return Promise.reject(image.status);
}
}

function ImageLocaliser(props: Props): JSX.Element {
const [currentState, setCurrentState] = useState<LocaliserState>('none');
const { setDeviceOptions, t, devices } = props;
const [localisationStatus, setLocalisationStatus] = useState<Record<IEEEEAddress, LStatus>>({});

async function localiseImage(device: Device) {
setLocalisationStatus((curr) => {
return { ...curr, [device.ieee_address]: 'init' };

});

const imageUrl = getZ2mDeviceImage(device);

const imageContent = await downloadImage(imageUrl);
console.log(device.friendly_name, imageUrl, imageContent);
await setDeviceOptions(device.ieee_address, { icon: imageContent });
setLocalisationStatus((curr) => {
return { ...curr, [device.ieee_address]: 'done' };
});
}
useEffect(() => {
if (currentState == 'start') {
for (const device of Object.values<Device>(devices)
.filter(d => d.type !== 'Coordinator')) {
localiseImage(device).catch(err => {
setLocalisationStatus((curr) => {
return { ...curr, [device.ieee_address]: 'error' };
});
}).then();
}
setCurrentState('inprogress');
}
}, [currentState]);

switch (currentState) {
case 'none':
return <Button className="btn btn-primary d-block mt-2" onClick={() => setCurrentState('start')}>
{t('localise_images')}
</Button>
case 'inprogress':
return <div>{
Object.values(devices).map((device) => {
return <div key={device.ieee_address}>{device.friendly_name} {localisationStatus[device.ieee_address]}</div>
})
}</div>
case 'done':
return <div>done</div>
}
return <div>Unknown</div>

}

export default withTranslation(['settings'])(ImageLocaliser);
9 changes: 6 additions & 3 deletions src/components/settings-page/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import frontentPackageJson from '../../../package.json';
import { formatDate } from '../../utils';
import { saveAs } from 'file-saver';
import Spinner from '../spinner';
import ImageLocaliser from './image-localiser';
import { DeviceApi } from '../../actions/DeviceApi';

type SettingsTab = 'settings' | 'bridge' | 'about' | 'tools' | 'donate' | 'translate';

Expand Down Expand Up @@ -115,7 +117,7 @@ type PropsFromStore = Pick<
'bridgeInfo' | 'missingTranslations' | 'devices' | 'backup' | 'preparingBackup'
>;
export class SettingsPage extends Component<
PropsFromStore & SettingsPageProps & BridgeApi & UtilsApi & WithTranslation<'setting'>,
PropsFromStore & SettingsPageProps & DeviceApi & BridgeApi & UtilsApi & WithTranslation<'setting'>,
SettingsPageState
> {
state = {
Expand Down Expand Up @@ -298,7 +300,7 @@ export class SettingsPage extends Component<
};

renderTools(): JSX.Element {
const { exportState, restartBridge, t } = this.props;
const { exportState, restartBridge, setDeviceOptions, devices, t } = this.props;
return (
<div className="p-3">
<Button className="btn btn-primary d-block mt-2" onClick={exportState}>
Expand All @@ -311,6 +313,7 @@ export class SettingsPage extends Component<
<Button className="btn btn-primary d-block mt-2" onClick={this.addInstallCode}>
{t('add_install_code')}
</Button>
<ImageLocaliser setDeviceOptions={setDeviceOptions} devices={devices}/>
</div>
);
}
Expand Down Expand Up @@ -436,7 +439,7 @@ export class SettingsPage extends Component<
const SettingsPageWithRouter = withRouter(SettingsPage);
const mappedProps = ['bridgeInfo', 'missingTranslations', 'devices', 'backup', 'preparingBackup'];
const ConnectedSettingsPage = withTranslation(['settings', 'common'])(
connect<Record<string, unknown>, Record<string, unknown>, GlobalState, BridgeApi>(
connect<Record<string, unknown>, Record<string, unknown>, GlobalState, DeviceApi & BridgeApi>(
mappedProps,
actions,
)(SettingsPageWithRouter),
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2249,7 +2249,8 @@
"coordinator_ieee_address": "Coordinator IEEE Address",
"request_z2m_backup": "Request Z2m backup",
"add_install_code": "Add install code",
"homeassistant": "Home Assistant integration"
"homeassistant": "Home Assistant integration",
"localise_images": "Localise device images"
},
"touchlink": {
"detected_devices_message": "Detected {{count}} touchlink devices.",
Expand Down

0 comments on commit db81940

Please sign in to comment.