Skip to content

Commit

Permalink
Philips Hue: Do n-Upnp scan + Upnp scan + manual add of bridge (#1920)
Browse files Browse the repository at this point in the history
  • Loading branch information
cicoub13 authored Nov 10, 2023
1 parent 86cd2dd commit 7c173cf
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 146 deletions.
9 changes: 7 additions & 2 deletions front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,13 @@
"connectButton": "Connect/Reconnect",
"scanButton": "Scan network",
"bridgeButtonNotPressed": "Bridge button not pressed: Please press the button on your Philips Hue bridge and try again.",
"unknownError": "An unknown error occured. Please try again or contact Gladys community.",
"noBridgesFound": "We didn't find any Philips Hue bridges on your network. Are you sure you are connected to the same network as your bridge and your bridge is turned on?"
"unknownError": "An unknown error occurred. Please try again or contact Gladys community.",
"noBridgesFound": "We didn't find any Philips Hue bridges on your network. Are you sure you are connected to the same network as your bridge and your bridge is turned on?",
"manualConfiguration": {
"title": "Manual configuration",
"text": "If you know the IP address of your Philips Hue bridge, you can fill it and launch a manual connection.",
"input": "IP address"
}
},
"device": {
"title": "Devices in Gladys",
Expand Down
7 changes: 6 additions & 1 deletion front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,12 @@
"scanButton": "Recherche sur le réseau",
"bridgeButtonNotPressed": "Le bouton du pont n'a pas été appuyé : veuillez appuyer sur le bouton de votre pont Philips Hue et réessayer.",
"unknownError": "Une erreur inconnue s'est produite. Veuillez réessayer ou contacter <a href =\"https://community.gladysassistant.com\">Gladys community</a>.",
"noBridgesFound": "Nous n'avons trouvé aucun pont Philips Hue sur votre réseau. Êtes-vous sûr que vous êtes connecté au même réseau que votre pont et que celui-ci est sous tension?"
"noBridgesFound": "Nous n'avons trouvé aucun pont Philips Hue sur votre réseau. Êtes-vous sûr que vous êtes connecté au même réseau que votre pont et que celui-ci est sous tension?",
"manualConfiguration": {
"title": "Configuration manuelle",
"text": "Si vous connaissez l'adresse IP de votre pont Philips Hue, vous pouvez la saisir et lancer une configuration manuelle.",
"input": "Adresse IP"
}
},
"device": {
"title": "Appareils dans Gladys",
Expand Down
228 changes: 134 additions & 94 deletions front/src/routes/integration/all/philips-hue/setup-page/SetupTab.jsx
Original file line number Diff line number Diff line change
@@ -1,126 +1,166 @@
import { MarkupText, Text } from 'preact-i18n';
import { Component } from 'preact';
import { MarkupText, Text, Localizer } from 'preact-i18n';
import cx from 'classnames';
import { RequestStatus } from '../../../../../utils/consts';
import style from './style.css';

const disconnectBridge = (props, device, index) => () => {
props.deleteDevice(device, index);
};
class SetupTab extends Component {
disconnectBridge = (props, device, index) => () => {
props.deleteDevice(device, index);
};

const connectBridge = (props, device) => () => {
props.connectBridge(device);
};
connectBridge = (props, device) => () => {
props.connectBridge(device);
};

const SetupTab = ({ children, ...props }) => {
return (
<div>
{props.philipsHueBridgesDevices && props.philipsHueBridgesDevices.length > 0 && (
setmanualBridgeConfiguration = ipaddress => {
this.setState({ manualBridgeConfiguration: { ipaddress: ipaddress.target.value } });
};

render(props) {
return (
<div>
{props.philipsHueBridgesDevices && props.philipsHueBridgesDevices.length > 0 && (
<div class="card">
<div class="card-header">
<h3 class="card-title">
<Text id="integration.philipsHue.setup.connectedBridgesTitle" />
</h3>
</div>
<div class="card-body">
<div
class={cx('dimmer', {
active: props.philipsHueGetDevicesStatus === RequestStatus.Getting
})}
>
<div class="loader" />
<div class="dimmer-content">
{props.philipsHueDeleteDeviceStatus === RequestStatus.Error && (
<p class="alert alert-danger">
<MarkupText id="integration.philipsHue.setup.unknownError" />
</p>
)}
{props.philipsHueGetDevicesStatus === RequestStatus.Getting && <div class={style.emptyDiv} />}
<div class="row">
{props.philipsHueBridgesDevices &&
props.philipsHueBridgesDevices.map((bridge, index) => (
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h3 class="card-title">{bridge.name}</h3>
</div>
<div class="card-body">
<button class="btn btn-danger" onClick={this.disconnectBridge(props, bridge, index)}>
<Text id="integration.philipsHue.setup.disconnectButton" />
</button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
)}
<div class="card">
<div class="card-header">
<h3 class="card-title">
<Text id="integration.philipsHue.setup.connectedBridgesTitle" />
<Text id="integration.philipsHue.setup.bridgesOnNetwork" />
</h3>
<div class="page-options d-flex">
<button
class="btn btn-info"
onClick={props.getBridges}
disabled={props.philipsHueGetBridgesStatus === RequestStatus.Getting}
>
<i class="fe fe-radio" /> <Text id="integration.philipsHue.setup.scanButton" />
</button>
</div>
</div>
<div class="card-body">
<div
class={cx('dimmer', {
active: props.philipsHueGetDevicesStatus === RequestStatus.Getting
active:
props.philipsHueGetBridgesStatus === RequestStatus.Getting ||
props.philipsHueCreateDeviceStatus === RequestStatus.Getting
})}
>
<div class="loader" />
<div class="dimmer-content">
{props.philipsHueDeleteDeviceStatus === RequestStatus.Error && (
<p class="alert alert-danger">
<MarkupText id="integration.philipsHue.setup.unknownError" />
</p>
)}
{props.philipsHueGetDevicesStatus === RequestStatus.Getting && <div class={style.emptyDiv} />}
<div class="row">
{props.philipsHueBridgesDevices &&
props.philipsHueBridgesDevices.map((bridge, index) => (
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h3 class="card-title">{bridge.name}</h3>
{props.philipsHueGetBridgesStatus === RequestStatus.Getting && <div class={style.emptyDiv} />}
{props.philipsHueBridges && props.philipsHueBridges.length === 0 && (
<div class="col-md-12">
<div class="alert alert-info">
<Text id="integration.philipsHue.setup.noBridgesFound" />
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="card-title">
<Text id="integration.philipsHue.setup.manualConfiguration.title" />
</h3>
</div>
<div class="card-body">
<div class="form-group">
<Text id="integration.philipsHue.setup.manualConfiguration.text" />
</div>
<div class="form-group">
<Localizer>
<input
id="manualConfiguration"
type="text"
class="form-control"
onChange={this.setmanualBridgeConfiguration}
placeholder={<Text id="integration.philipsHue.setup.manualConfiguration.input" />}
/>
</Localizer>
</div>
<div class="card-body">
<button class="btn btn-danger" onClick={disconnectBridge(props, bridge, index)}>
<Text id="integration.philipsHue.setup.disconnectButton" />
<div class="form-group">
<button
class="btn btn-success"
onClick={this.connectBridge(props, this.state.manualBridgeConfiguration)}
>
<Text id="integration.philipsHue.setup.connectButton" />
</button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
)}
<div class="card">
<div class="card-header">
<h3 class="card-title">
<Text id="integration.philipsHue.setup.bridgesOnNetwork" />
</h3>
<div class="page-options d-flex">
<button
class="btn btn-info"
onClick={props.getBridges}
disabled={props.philipsHueGetBridgesStatus === RequestStatus.Getting}
>
<i class="fe fe-radio" /> <Text id="integration.philipsHue.setup.scanButton" />
</button>
</div>
</div>
<div class="card-body">
<div
class={cx('dimmer', {
active:
props.philipsHueGetBridgesStatus === RequestStatus.Getting ||
props.philipsHueCreateDeviceStatus === RequestStatus.Getting
})}
>
<div class="loader" />
<div class="dimmer-content">
{props.philipsHueGetBridgesStatus === RequestStatus.Getting && <div class={style.emptyDiv} />}
{props.philipsHueBridges && props.philipsHueBridges.length === 0 && (
<div class="col-md-12">
<div class="alert alert-info">
<Text id="integration.philipsHue.setup.noBridgesFound" />
</div>
</div>
</div>
)}
{props.philipsHueCreateDeviceStatus === RequestStatus.PhilipsHueBridgeButtonNotPressed && (
<p class="alert alert-danger">
<Text id="integration.philipsHue.setup.bridgeButtonNotPressed" />
</p>
)}
{props.philipsHueCreateDeviceStatus === RequestStatus.Error && (
<p class="alert alert-danger">
<MarkupText id="integration.philipsHue.setup.unknownError" />
</p>
)}
{props.philipsHueBridges &&
props.philipsHueBridges.map(bridge => (
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h3 class="card-title">{bridge.name}</h3>
</div>
<div class="card-body">
<button class="btn btn-success" onClick={connectBridge(props, bridge)}>
<Text id="integration.philipsHue.setup.connectButton" />
</button>
)}
{props.philipsHueCreateDeviceStatus === RequestStatus.PhilipsHueBridgeButtonNotPressed && (
<p class="alert alert-danger">
<Text id="integration.philipsHue.setup.bridgeButtonNotPressed" />
</p>
)}
{props.philipsHueCreateDeviceStatus === RequestStatus.Error && (
<p class="alert alert-danger">
<MarkupText id="integration.philipsHue.setup.unknownError" />
</p>
)}
{props.philipsHueBridges &&
props.philipsHueBridges.map(bridge => (
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h3 class="card-title">{bridge.name}</h3>
</div>
<div class="card-body">
<button class="btn btn-success" onClick={this.connectBridge(props, bridge)}>
<Text id="integration.philipsHue.setup.connectButton" />
</button>
</div>
</div>
</div>
</div>
))}
))}
</div>
</div>
</div>
</div>
</div>
</div>
);
};
);
}
}

export default SetupTab;
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const actions = store => ({
});
try {
const createdDevice = await state.httpClient.post('/api/v1/service/philips-hue/bridge/configure', {
serial: bridge.model.serial
ipAddress: bridge.ipaddress
});
const newState = update(state, {
philipsHueBridgesDevices: {
Expand Down
4 changes: 2 additions & 2 deletions server/services/philips-hue/api/hue.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ module.exports = function HueController(philipsHueLightHandler) {
/**
* @api {post} /api/v1/service/philips-hue/bridge/configure Configure Philips Hue Bridge
* @apiName ConfigureBridge
* @apiParam {String} serial Serial number of the bridge
* @apiParam {String} ipAddress IP Address of the bridge
* @apiGroup PhilipsHue
*/
async function configureBridge(req, res) {
const bridge = await philipsHueLightHandler.configureBridge(req.body.serial);
const bridge = await philipsHueLightHandler.configureBridge(req.body.ipAddress);
res.json(bridge);
}

Expand Down
2 changes: 1 addition & 1 deletion server/services/philips-hue/lib/light/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const PhilipsHueLightHandler = function PhilipsHueLightHandler(gladys, hueClient
this.serviceId = serviceId;
this.bridges = [];
this.connnectedBridges = [];
this.bridgesBySerialNumber = new Map();
this.bridgesByIP = new Map();
this.hueApisBySerialNumber = new Map();
this.lights = [];
};
Expand Down
32 changes: 18 additions & 14 deletions server/services/philips-hue/lib/light/light.configureBridge.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const logger = require('../../../../utils/logger');
const { NotFoundError } = require('../../../../utils/coreErrors');
const { Error403 } = require('../../../../utils/httpErrors');
const {
HUE_DEVICE_NAME,
Expand All @@ -13,42 +12,47 @@ const {

/**
* @description Configure the philips hue bridge.
* @param {string} serialNumber - Serial number of the Philips Hue Bridge.
* @param {string} ipAddress - IP Address of the Philips Hue Bridge.
* @returns {Promise<object>} Resolve with created device.
* @example
* configureBridge('162.198.1.1');
*/
async function configureBridge(serialNumber) {
const bridge = this.bridgesBySerialNumber.get(serialNumber);
async function configureBridge(ipAddress) {
const bridge = this.bridgesByIP.get(ipAddress);
if (!bridge) {
throw new NotFoundError(`BRIDGE_NOT_FOUND`);
logger.info(`Connecting to hue bridge ip = ${ipAddress} manually (not from discovered bridges)...`);
} else {
logger.info(`Connecting to hue bridge "${bridge.name}", ip = ${ipAddress} (from discovered bridges)...`);
}
logger.info(`Connecting to hue bridge "${serialNumber}", ip = ${bridge.ipaddress}...`);
try {
const hueApi = this.hueClient.api;
const unauthenticatedApi = await hueApi.createLocal(bridge.ipaddress).connect();
const unauthenticatedApi = await hueApi.createLocal(ipAddress).connect();
const user = await unauthenticatedApi.users.createUser(HUE_APP_NAME, HUE_DEVICE_NAME);
const authenticatedApi = await hueApi.createLocal(bridge.ipaddress).connect(user.username);
this.hueApisBySerialNumber.set(serialNumber, authenticatedApi);
const authenticatedApi = await hueApi.createLocal(ipAddress).connect(user.username);
// Get configuration to fetch serialNumber
const bridgeConfig = await authenticatedApi.configuration.get();
const bridgeSerialNumber = bridgeConfig.bridgeid;
const bridgeName = bridge ? bridge.name : bridgeConfig.name;
this.hueApisBySerialNumber.set(bridgeSerialNumber, authenticatedApi);
const deviceCreated = await this.gladys.device.create({
name: bridge.name,
name: bridgeName,
service_id: this.serviceId,
external_id: `${BRIDGE_EXTERNAL_ID_BASE}:${serialNumber}`,
selector: `${BRIDGE_EXTERNAL_ID_BASE}:${serialNumber}`,
external_id: `${BRIDGE_EXTERNAL_ID_BASE}:${bridgeSerialNumber}`,
selector: `${BRIDGE_EXTERNAL_ID_BASE}:${bridgeSerialNumber}`,
model: BRIDGE_MODEL,
features: [],
params: [
{
name: BRIDGE_IP_ADDRESS,
value: bridge.ipaddress,
value: ipAddress,
},
{
name: BRIDGE_USERNAME,
value: user.username,
},
{
name: BRIDGE_SERIAL_NUMBER,
value: serialNumber,
value: bridgeSerialNumber,
},
],
});
Expand Down
Loading

0 comments on commit 7c173cf

Please sign in to comment.