# \[APP\]\[Pro\] Casambi Controller - Lighting control for the modern world

**URL:** <https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512>\
**Category:** Apps\
**Tags:** app\
**Created:** [February 6, 2021, 8:50pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512 "2021-02-06T20:50:59Z")\
**Posts on this page:** 20\
**Page:** 4

<div class="post-metadata">

**Author:** ![Martijn\_de\_Koning](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/martijn_de_koning/32/77766_2.png) [@Martijn\_de\_Koning](https://community.homey.app/u/Martijn_de_Koning)\
**Post date:** [May 3, 2024, 6:34pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/62 "2024-05-03T18:34:39Z")

</div>

Hèb je hier iets aan?

Here are several issues and potential improvements in the provided code snippet:

1. **getSocket Method Empty Implementation:**  
The `getSocket` method does not have any implementation, which could be intentional (e.g., for overriding in subclasses) but if not, it should either have a meaningful implementation or be documented as to why it’s empty.

2. **Reconnection Logic Could Be Improved:**  
The `reconnect` method clears a timer and attempts to delete a socket from an unspecified `this.sockets` object based on network properties. However, there’s no check to see if the socket actually exists before attempting deletion. Additionally, the reconnection is attempted immediately after deletion without any delay or failure handling, which might not be effective in all network conditions.

3. **Authentication Check Simplification:**  
The `isAuthenticated` method returns a boolean based on the existence of `this.networks`. This is overly simplistic as it does not actually check if the session is still valid. The commented part (`// TODO: && Date.now() < this.networks.expires_at`) should be implemented to check for session expiration.

4. **Login Method Error Handling and Redundancy:**  
The `login` method has an initial check `if (this.isLoggingIn)`, which is good for preventing simultaneous login attempts. However, the logic to handle this situation involves a mere 100ms delay before retrying, which may not be sufficient or necessary. Consider implementing a more robust method for handling simultaneous logins, such as using event-driven techniques or a longer, possibly exponential, backoff strategy.

5. **Exception Handling in Login:**  
When the login fails (`if (!response.ok)`), the method logs and then throws an error. This is generally fine, but the thrown error should also include the HTTP status code for better debugging and error handling.

6. **Use of Magic Strings and Numbers:**  
There are several “magic” strings and numbers (like the URL path and the 100ms timeout), which should ideally be defined as constants at the top of the class or module for easier maintenance and configuration.

7. **Headers Manipulation:**  
The `delete this.headers['X-Casambi-Session'];` operation directly manipulates headers without any check. It would be safer and clearer to manage headers in a more controlled manner, especially considering the role of headers in authentication processes.

By addressing these issues, the code can be made more robust, maintainable, and clear in its intent and handling of network operations and authentication.

Here’s a rewritten version of the provided code, incorporating better practices, including error handling, use of constants for configuration values, and more structured login control to handle concurrency effectively:

```typescript
class NetworkManager {
    private static readonly BASE_URL = 'https://api.example.com';
    private static readonly AUTHENTICATE_URL = `${NetworkManager.BASE_URL}/networks/session`;
    private static readonly RECONNECT_DELAY_MS = 1000; // 1 second delay for reconnection

    private isLoggingIn = false;
    private sockets: { [key: string]: WebSocket } = {};
    private networks: NetworkList | null = null;
    private headers: { [key: string]: string } = {};

    constructor(private username: string, private password: string) {}

    protected getSocket(network: Network): WebSocket {
        const key = `${network.id}-${network.sessionId}`;
        let socket = this.sockets[key];
        if (!socket) {
            socket = new WebSocket(NetworkManager.BASE_URL);
            this.sockets[key] = socket;
        }
        return socket;
    }

    protected reconnect(network: Network) {
        const key = `${network.id}-${network.sessionId}`;
        const existingTimer = this.sockets[key];
        if (existingTimer) {
            clearInterval(existingTimer as unknown as NodeJS.Timer);
            delete this.sockets[key];
            setTimeout(() => this.getSocket(network), NetworkManager.RECONNECT_DELAY_MS);
        }
    }

    protected isAuthenticated(): boolean {
        return this.networks !== null && Date.now() < (this.networks.expires_at ?? Number.MAX_VALUE);
    }

    protected async login(): Promise<NetworkList> {
        if (this.isLoggingIn) {
            await new Promise(resolve => setTimeout(resolve, NetworkManager.RECONNECT_DELAY_MS));
            return this.getNetworks();
        }

        this.isLoggingIn = true;
        delete this.headers['X-Casambi-Session'];

        try {
            const response = await fetch(NetworkManager.AUTHENTICATE_URL, {
                method: 'POST',
                body: JSON.stringify({ email: this.username, password: this.password }),
                headers: this.headers as HeadersInit,
            });

            if (!response.ok) {
                const errorText = await response.text();
                console.error(`Authentication failed for ${this.username}: ${errorText}`);
                throw new Error(`Authentication failed with status ${response.status}: ${errorText}`);
            }

            this.networks = await response.json() as NetworkList;
            console.log(`Authentication succeeded for ${this.username}`);
            return this.networks;
        } finally {
            this.isLoggingIn = false;
        }
    }

    private getNetworks(): NetworkList {
        if (!this.networks) {
            throw new Error("No network information available.");
        }
        return this.networks;
    }
}

```

### Key Changes and Improvements:

- **Constant Usage** : Constants are used for URLs and time delays to make configuration easier and the code cleaner.
- **Error Handling in Login** : The login function now includes better handling with try-finally to ensure `this.isLoggingIn` is reset properly.
- **Reconnect Delay** : A constant is used for the reconnection delay, and the reconnection logic checks if the socket exists before attempting to reconnect.
- **Session Validation** : The `isAuthenticated` method now correctly validates the session based on an expiration time.
- **Networks Getter** : A private method `getNetworks` safely retrieves network information, throwing an error if it’s not available.

This rewrite aims to enhance readability, maintainability, and error handling in the code.

---

<div class="post-metadata">

**Author:** ![Martijn\_de\_Koning](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/martijn_de_koning/32/77766_2.png) [@Martijn\_de\_Koning](https://community.homey.app/u/Martijn_de_Koning)\
**Post date:** [May 3, 2024, 6:48pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/63 "2024-05-03T18:48:20Z")

</div>

Dit is de volledige ChatGPT versie 😃

To address the identified issues and improve the design of the `Client` class, I will refactor the code to enhance modularity, readability, and error handling. Here’s an improved version of the TypeScript class with some additional comments and adjustments:

```typescript
import fetch, { HeadersInit } from 'node-fetch';
import WebSocket from 'ws';

const BASE_URL = 'https://door.casambi.com/v1';
const RECONNECT_INTERVAL_MS = 240000; // 4 minutes
const LOGIN_RETRY_DELAY_MS = 1000; // 1 second

interface Network {
    id: string;
    sessionId: string;
}

interface NetworkList {
    [id: string]: Network;
}

interface DeviceEventCallback {
    (data: any): void;
}

class WebSocketManager {
    private socket: WebSocket | null = null;
    private intervalId: NodeJS.Timeout | null = null;

    constructor(private token: string, private network: Network) {}

    connect() {
        const socketUrl = `wss://door.casambi.com/v1/bridge/${this.token}`;
        this.socket = new WebSocket(socketUrl);

        this.socket.on('open', () => {
            this.keepAlive();
        });

        this.socket.on('close', () => {
            this.reconnect();
        });

        this.socket.on('error', error => {
            console.error('WebSocket Error:', error);
            this.reconnect();
        });
    }

    send(data: string) {
        if (this.socket?.readyState === WebSocket.OPEN) {
            this.socket.send(data);
        }
    }

    private keepAlive() {
        this.intervalId = setInterval(() => {
            if (this.socket?.readyState === WebSocket.OPEN) {
                const ping = JSON.stringify({ method: 'ping' });
                this.socket.send(ping);
            }
        }, RECONNECT_INTERVAL_MS);
    }

    private reconnect() {
        if (this.intervalId) {
            clearInterval(this.intervalId);
        }
        this.connect();
    }

    close() {
        if (this.intervalId) {
            clearInterval(this.intervalId);
        }
        if (this.socket) {
            this.socket.close();
        }
    }
}

class Client {
    private isLoggingIn = false;
    private networks?: NetworkList;
    private headers: HeadersInit = {
        'Content-Type': 'application/json',
        'X-Casambi-Key': '',
    };
    private webSocketManager?: WebSocketManager;

    constructor(private token: string, private username: string, private password: string) {
        this.headers['X-Casambi-Key'] = token;
    }

    async authenticate(): Promise<void> {
        if (this.isLoggingIn) {
            await new Promise(resolve => setTimeout(resolve, LOGIN_RETRY_DELAY_MS));
            return;
        }

        this.isLoggingIn = true;
        const url = `${BASE_URL}/networks/session`;
        const body = JSON.stringify({ email: this.username, password: this.password });

        const response = await fetch(url, {
            method: 'POST',
            headers: this.headers,
            body: body,
        });

        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`Authentication failed: ${errorText}`);
        }

        this.networks = await response.json() as NetworkList;
        this.isLoggingIn = false;
    }

    async getNetwork(networkId: string): Promise<Network> {
        if (!this.networks) {
            throw new Error('No networks available');
        }
        return this.networks[networkId];
    }

    connectToNetwork(networkId: string) {
        if (!this.networks) {
            throw new Error('Not authenticated or no networks available');
        }
        const network = this.networks[networkId];
        this.webSocketManager = new WebSocketManager(this.token, network);
        this.webSocketManager.connect();
    }

    disconnectFromNetwork() {
        if (this.webSocketManager) {
            this.webSocketManager.close();
        }
    }

    async testCredentials(): Promise<boolean> {
        try {
            await this.authenticate();
            return true;
        } catch {
            return false;
        }
    }
}

export default Client;

```

### Key Changes Made:

1. **Modular Design** : Separated WebSocket handling into its own class, `WebSocketManager`, to encapsulate WebSocket lifecycle management, including connection, keep-alive, and error handling.

2. **Error Handling** : Improved throughout, particularly in network requests, with more precise error messages.

3. **Authentication Method** : Renamed `login` to `authenticate` for clarity and adjusted to prevent multiple simultaneous login attempts more effectively.

4. **Connection Management** : Methods `connectToNetwork` and `disconnectFromNetwork` manage network connections, leveraging the `WebSocketManager`.

5. **Configuration Constants** : Introduced constants for timings and URLs for easier modification and clearer code.

This refactoring improves the overall structure, making the code more maintainable, secure, and easier to debug while

---

<div class="post-metadata">

**Author:** ![LvanderRee](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/lvanderree/32/1853_2.png) [@LvanderRee](https://community.homey.app/u/LvanderRee)\
**Post date:** [May 4, 2024, 9:49am UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/64 "2024-05-04T09:49:57Z")

</div>

Nice one @Martijn_de_Koning I haven’t checked the code in detail yet, but from what I’ve scanned in it’s observations it might have found the cause and has provided a solution.

As soon as I have some time I will try to implement it

---

<div class="post-metadata">

**Author:** ![Martijn\_de\_Koning](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/martijn_de_koning/32/77766_2.png) [@Martijn\_de\_Koning](https://community.homey.app/u/Martijn_de_Koning)\
**Post date:** [May 5, 2024, 12:00pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/65 "2024-05-05T12:00:17Z")

</div>

Any update Leon? 😃 Niets werkt namelijk nu en dat is erg onhandig 😅

---

<div class="post-metadata">

**Author:** ![Wouter\_de\_Vaal](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/wouter_de_vaal/32/85081_2.png) [@Wouter\_de\_Vaal](https://community.homey.app/u/Wouter_de_Vaal)\
**Post date:** [May 10, 2024, 10:06am UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/66 "2024-05-10T10:06:50Z")

</div>

Dear,

So I tried to login at my Casambi network but is says; The credentials are not right.  
Does anyone else have the same problem?

---

<div class="post-metadata">

**Author:** ![Mattias\_Ulen](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/mattias_ulen/32/71584_2.png) [@Mattias\_Ulen](https://community.homey.app/u/Mattias_Ulen)\
**Post date:** [May 14, 2024, 2:35pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/67 "2024-05-14T14:35:39Z")

</div>

I have exactly the same problem, can’t log in to Casambi cloud.

Best regards

Mattias Ulén

---

<div class="post-metadata">

**Author:** ![LvanderRee](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/lvanderree/32/1853_2.png) [@LvanderRee](https://community.homey.app/u/LvanderRee)\
**Post date:** [May 14, 2024, 3:26pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/68 "2024-05-14T15:26:41Z")

</div>

People read: [[App][Pro] Casambi - #60 by Vesa\_Vainio](https://community.homey.app/t/app-pro-casambi/42512/60)  
**Casambi noticed the app is flawed!**

@Martijn_de_Koning proposed a solution with the help of ChatGPT, but I haven’t got the time to take a proper look at it yet and implement a fix accordingly.

I hope to have some time this week to update the app and see if this fixes the issue. To be updated

---

<div class="post-metadata">

**Author:** ![Meso](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/meso/32/79712_2.png) [@Meso](https://community.homey.app/u/Meso)\
**Post date:** [May 21, 2024, 8:24pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/69 "2024-05-21T20:24:31Z")

</div>

Thought my setup was broken, happy to see a fix is being worked on 🙂

---

<div class="post-metadata">

**Author:** ![Karel\_Nolly](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/karel_nolly/32/78999_2.png) [@Karel\_Nolly](https://community.homey.app/u/Karel_Nolly)\
**Post date:** [May 23, 2024, 12:11pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/70 "2024-05-23T12:11:52Z")

</div>

Dear Vesa Vainio  
Thanks for your information. I am not sure, but is there no interest of casambi to build the homey connection by yourself, so that it will work properly for all of the users?  
I just bought a casambi Cloud Gateway and hoped this would help to connect with homey.  
I look forward to hear from you!  
Karel

---

<div class="post-metadata">

**Author:** ![Vesa\_Vainio](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/vesa_vainio/32/80597_2.png) [@Vesa\_Vainio](https://community.homey.app/u/Vesa_Vainio)\
**Post date:** [May 23, 2024, 1:49pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/71 "2024-05-23T13:49:55Z")

</div>

Hello Karel,

Unfortunately, we at Casambi don’t have the resources to contribute development work for the Homey app. We are not familiar with the Homey platform, or developing apps for the Homey platform at all. We don’t have any Homey devices and thus no possibility to test the app ourselves.

This is also how we usually work. We have hundreds of partners creating and selling Casambi-enabled products. The partners do their own development, and we provide help with the relevant Casambi interfaces, and also develop features on our systems when necessary and feasible.

Our general API documentation is available at [https://developer.casambi.com/](https://developer.casambi.com/) and we can try to help by e.g. answering questions about the documentation or the API functionality.

I’m also having a direct discussion with one of the community members about the possibilities for testing a modified version of the app.

Best regards,  
Vesa Vainio

---

<div class="post-metadata">

**Author:** ![Karel\_Nolly](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/karel_nolly/32/78999_2.png) [@Karel\_Nolly](https://community.homey.app/u/Karel_Nolly)\
**Post date:** [May 23, 2024, 3:22pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/72 "2024-05-23T15:22:33Z")

</div>

Thank you very much for your quick answer. As I am “only” a user and not a programmer I would be very happy to use my Casambi Installation (35 products) with homey

---

<div class="post-metadata">

**Author:** ![Martijn\_de\_Koning](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/martijn_de_koning/32/77766_2.png) [@Martijn\_de\_Koning](https://community.homey.app/u/Martijn_de_Koning)\
**Post date:** [May 24, 2024, 9:52am UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/74 "2024-05-24T09:52:13Z")

</div>

The app has been modified. The code changes have been communicated to Casambi. Now we wait until the API is made available again.

---

<div class="post-metadata">

**Author:** ![Mattias\_Ulen](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/mattias_ulen/32/71584_2.png) [@Mattias\_Ulen](https://community.homey.app/u/Mattias_Ulen)\
**Post date:** [May 27, 2024, 9:41pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/75 "2024-05-27T21:41:53Z")

</div>

After Casambi Controller update to ver 0.4.2, everything works perfect again. You have to reinstall the app.

THANK YOU! 👍

---

<div class="post-metadata">

**Author:** ![Jens\_Burgers](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/jens_burgers/32/78856_2.png) [@Jens\_Burgers](https://community.homey.app/u/Jens_Burgers)\
**Post date:** [May 29, 2024, 8:28pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/76 "2024-05-29T20:28:35Z")

</div>

Dag Martijn, bedankt voor het aanpassen en werkend krijgen van de Homey Casambi App. Helaas doen de flows het niet, althans. Ik heb flows die meerdere lichten uit of aan zetten, dimmen, etc. Voorbeeld drie lichten in een flow uit, leidt tot slechts een licht uit. Aan geeft ook issues. Het lijkt of er niet parallel kan worden geschakeld. Groeten, Jens

---

<div class="post-metadata">

**Author:** ![Martijn\_de\_Koning](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/martijn_de_koning/32/77766_2.png) [@Martijn\_de\_Koning](https://community.homey.app/u/Martijn_de_Koning)\
**Post date:** [May 29, 2024, 8:52pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/77 "2024-05-29T20:52:29Z")

</div>

Hi Jens,

Welke versie van de ap gebruik je?

Maak je gebruik van de Casambi gateway of bijv. een Iphone die als Gateway wordt gebruikt?

---

<div class="post-metadata">

**Author:** ![Jens\_Burgers](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/jens_burgers/32/78856_2.png) [@Jens\_Burgers](https://community.homey.app/u/Jens_Burgers)\
**Post date:** [May 29, 2024, 9:30pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/78 "2024-05-29T21:30:44Z")

</div>

Hi Martijn,

Ik gebruik de 2.0.15 versie en de Casambi Gateway.

Groeten, Jens

---

<div class="post-metadata">

**Author:** ![Martijn\_de\_Koning](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/martijn_de_koning/32/77766_2.png) [@Martijn\_de\_Koning](https://community.homey.app/u/Martijn_de_Koning)\
**Post date:** [May 29, 2024, 9:44pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/79 "2024-05-29T21:44:00Z")

</div>

Hi Jens,

Met de Casambi Gateway heb ik hetzelfde probleem. Dit moet ik verder uitzoeken. Als tijdelijke oplossing adviseer ik bijv. een Iphone als Gateway te gebruiken.

Gr,

---

<div class="post-metadata">

**Author:** ![Jens\_Burgers](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/jens_burgers/32/78856_2.png) [@Jens\_Burgers](https://community.homey.app/u/Jens_Burgers)\
**Post date:** [May 29, 2024, 9:45pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/80 "2024-05-29T21:45:35Z")

</div>

Okay, thanks.

---

<div class="post-metadata">

**Author:** ![Arjan\_Kuijper](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/arjan_kuijper/32/79084_2.png) [@Arjan\_Kuijper](https://community.homey.app/u/Arjan_Kuijper)\
**Post date:** [May 30, 2024, 7:17am UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/81 "2024-05-30T07:17:39Z")

</div>

Dank voor deze update, alles werkt weer! Gebruik een Mac als gateway en geen problemen

---

<div class="post-metadata">

**Author:** ![Rein\_Baardsen](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/rein_baardsen/32/55948_2.png) [@Rein\_Baardsen](https://community.homey.app/u/Rein_Baardsen)\
**Post date:** [June 22, 2024, 6:48pm UTC](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512/82 "2024-06-22T18:48:48Z")

</div>

Does anyone have experience in whether the Ansell Octo Casambi Gateway works with this app?

[Previous page](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512.md?page=3)

[Next page](https://community.homey.app/t/app-pro-casambi-controller-lighting-control-for-the-modern-world/42512.md?page=5)
