Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 138 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

PlaceOS

Loading...

Overview

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

How To

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Tutorials

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Key Concepts

Interfaces

Interfaces are ways for users to interact with PlaceOS

Interfaces are web pages that provide users with a direct method of interaction with PlaceOS. They are suitable for any experiences that lend themselves to screen-based interaction. Common examples of interfaces are:

  • A staff app

  • Meeting room control interfaces

  • A booking system

PlaceOS Documentation

Explanations, how-to guides, tutorials and technical references for working with and building on PlaceOS.

PlaceOS is a software platform which enriches interaction with physical space. It provides tools to design, build and iterate integrated digital experiences for physical environments. To do this, it merges any and all elements - from lighting and AV to entire BMS - without the legacy baggage.

Integration of disparate systems, devices and services forms a seamless experience for your space.

Automation boosts workplace efficiency and ease of use for any space.

Iteration lets your space constantly improve and grow with you and your needs.

Sections of this site

The documentation for PlaceOS is split into 4 categories.

See the overview for high-level coverage of topic areas.

Try a tutorial to learn in more detail about a process or feature.

Read a how-to guide for docs to help you with a specific task.

See the reference material for detailed technical docs.

Videos and Demonstration Apps

You can watch a short or in-depth video demonstrating the functions of PlaceOS Backoffice.

Take our Workplace or Concierge Demo Apps for a spin.

Languages

Step 2: User Authentication & Calendar Access

Steps required for enabling OAuth2 sign and Room Booking for PlaceOS with Azure.

These steps will configure PlaceOS to use Microsoft Azure Active Directory as a federated authentication provider to allow users to sign in using their corporate credentials. The same Azure App Registration will be used to allow users logged in to PlaceOS Apps to book Exchange Online meeting rooms.

PlaceOS prefers to use OAuth2.

One advantage of using OAuth2 over SAML is that it is possible to require individuals to authorise access to certain resources. Thus users grant access to PlaceOS which can maintain a refresh token for offline access as needed.

Prerequisites

  • Confirm the final UAT and PROD URLs of the web apps

  • Ensure that the DNS entries for these URLs are active and forwarding to the server(s)

  • Ensure that the SSL certificates for the above domains are signed and recognized as secure

Deployment

Notifications

User Interfaces

Drivers

Overview of the 2 types of drivers

Drivers are the core components of the PlaceOS platform. They combine to help different parts of the digital ecosystem interact with each other. PlaceOS has drivers that fall into one of two categories:

Integration Drivers

  • Communicate with external systems and lets them talk to PlaceOS

  • Represent hardware or software platforms (i.e. device or service)

  • Control any functionality of the external systems and handle any incoming data

Logic Drivers

  • Coordinate interactions between modules

  • Don't map to specific physical objects

  • Represent abstract or conceptual functions

  • May use a variety of devices or software platforms

Drivers and Modules

Modules are instances of drivers, letting the rest of PlaceOS access their specific functions.

Driver Map

The driver map shows driver dependencies in PlaceOS.

Modules

Modules are instances of drivers

Modules are instances of . Each module represents either:

  • A specific physical device controlled by PlaceOS,

  • A specific digital platform, or

  • Logic that controls how a specific set of hardware and software should interact

Purpose

All modules have two broad functions on the system they control:

  1. State: status information about the integration or higher level logic they control. Some examples of this kind of data could be:

    • Power status

    • Upcoming booking info

    • Current user

  2. Behavior: actions which the device, service, or logic can do. Some examples of these actions could be:

    • Power on/off

    • Create/edit booking

    • Post a chat message

Starting and Stopping Modules

Each module can be individually started or stopped at any time. When started, the driver connects to the physical or digital integration and keeps track of its status. When stopped, the driver disconnects from its integration and will not send or receive any data or commands. For logic modules, this enables / disables its functionality.

Modules and Systems

Modules must be a part of at least one , but can be part of more than one system. Each system can use the same module instance everywhere it's required. Examples of modules used this way could be:

  • A lighting gateway

  • Centrally installed audio-visual equipment

  • A common service such as a chatbot integration

Logic Modules

Logic modules are different to all other types of Modules (e.g. Device/Service modules) as they do not communicate to external devices/services. They only have access to the other Modules within the same System and co-ordinate actions across then.

As such, Logic Modules are strictly bound to the first System in which they are added, and should not be added to multiple Systems.

Settings Inheritance

Logic modules inherit settings from both the Driver from which they are instantiated and the System in which they are added (including the settings that the System has inherited from it's Zones). System settings override Driver settings.

Driver settings > Zone Settings (in the hierarchical order specified by the System) > System Settings > Logic Module Settings

All other Module types only inherit Settings from the Driver from which they are instantiated.

Driver settings > Logic Module Settings

Settings

Settings can be configured at any level

Settings are the configuration information that define how a deployment should behave. Any level can have defined settings, which are ultimately consumed by . , , or can have settings defined on them. Together, these create a system configuration that is easier to manage at scale.

Examples of some common uses for settings are:

  • Available video inputs/outputs

  • Source names

  • DSP block IDs

  • Lighting control IDs

  • Device authorization information

  • Desk / room auto-release timeouts

Within driver files are definitions for naming conventions and expected values for the settings. They will vary based on each deployment, but the general structure will always be similar.

JSON definitions

Settings are expressed as , that is, key-value pairs:

JSON is a common data-interchange format designed to be readable for humans, and for machines to parse and generate. If it's a new concept, you can

Settings inheritance

Different layers define settings which then combine to produce the final configuration. This simplifies large deployments, standardizes systems and reduces management overhead.

Systems inherit all the settings from each zone that they are in. Zones pass down their settings to all systems within them. Similarly, Modules inherit all the settings from the driver that they instantiate.

Only Systems and Modules inherit Settings

Systems

Systems inherit settings from their Zones, in the order specified on their Zones tab (highest priority at top).

Zone Settings (in the hierarchical order specified by the System) > System Settings

Modules

Logic modules inherit settings from both the Driver from which they are instantiated and the System in which they are added (including the settings that the System has inherited from it's Zones). System settings override Driver settings.

Driver settings > Zone Settings (in the hierarchical order specified by the System) > System Settings > Logic Module Settings

All other Module types (e.g. Device, Service) only inherit Settings from the Driver from which they are instantiated.

Driver settings > Logic Module Settings

Specific Overrules General

Settings inherited from a zone or driver combine with any settings defined directly on the system or module. If an inherited setting has the same key as one defined directly, the latter will override the inherited one. This lets you write general settings at a higher common level, with more specific ones on each lower tier.

{
  "key": "value",
  "foo": [1, 2, 3],
  "bar": true 
  "baz": { 
    "qux": 1.234
  }
}
modules
Zones
systems
drivers
modules
JSON data
learn more here

Triggers

Triggers can add simple logic to the system

Triggers provide a way to dynamically link state and behavior across different modules. They define actions that will occur based on certain criteria, such as:

  • System state

  • Time

  • External input such as a webhook

Customization

Triggers permit modules to influence the behavior of other modules directly. In PlaceOS Backoffice, users can create, assign and manage triggers. This way, they can customize extensive event driven behavior. That accompanies more complex core system behavior written in the logic modules.

TypeScript

TypeScript is an open-source language which builds on JavaScript

Overview

TypeScript is an open-source language which builds on JavaScript by adding static type definitions. Types provide a way to describe the shape of an object, provide better documentation, and allow TypeScript to validate your code.

TypeScript and Angular

Angular applications must be built with TypeScript. As PlaceOS frontend applications are mainly written in Angular, they need TypeScript.

Versioning

TypeScript version depends on your Angular version.

PlaceOS Frontends uses Angular 12 and TypeScript 4.2.4.

TypeScript References and Tooling

The TypeScript Handbook provides detailed documentation specifically related to TypeScript.

PlaceOS uses NX tools to assist with testing and building JavaScript Applications.

NX adds Jest for Unit Testing and Cypress for Integration and End-to-End Testing.

The Angular CLI lets you develop, scaffold and maintain Angular applications. It's used with PlaceOS frontend applications.

Automated Browser Testing

Automated browser testing can check application test cases. Any Selenium based automated test suite would be suitable for this purpose. One possible tool is Katalon Studio.

*[CLI]: Command-line Interface

Protocols

SAML

SAML Standard

Overview

Security Assertion Markup Language (SAML) is an open standard that allows Identity Providers (IdP) to pass authorization credentials to Service Providers (SP).

SAML is an umbrella standard that covers federation, identity management and SSO.

The SAML specification defines three roles:

  • The principal (typically a human user)

  • The Service Provider (SP)

  • The Identity Provider (IdP)

In the primary use case addressed by SAML, the principal requests a service from the Service Provider. The Service Provider requests and obtains an authentication assertion from the Identity Provider.

SAML in PlaceOS

By default, PlaceOS uses a local authentication method. PlaceOS also supports Federated Authentication via SAML as the advised method of user authentication.

Under this configuration, by the SAML Standard, PlaceOS is the Service Provider (SP).

Authentication providers can be associated with Domains in PlaceOS.

  • Configure SAML in PlaceOS

  • Add Domains in PlaceOS

Resources

  • Security Assertion Markup Language (SAML) V2.0 Technical Overview

Configure PlaceOS for Microsoft 365

Steps required to configure and integrate the PlaceOS Platform with Microsoft 365 for user authentication and room booking.

Microsoft Graph Delegated Permissions are used for users logged into PlaceOS web apps (like the Workplace app).

PlaceOS Drivers require Microsoft Graph Application Permissions, as there is no user involved in their authentication/usage.

For more information, see: https://learn.microsoft.com/en-us/graph/auth/auth-concepts#access-scenarios

When using Delegated Permissions some limitations are imposed on the Concierge Application - most notably the ability to edit user calendar bookings is removed. All other functionality such as viewing scheduled meetings and catering is retained, as long as the logged in user has permission to view those calendars.

These steps will help Azure Administrators make the necessary configurations for PlaceOS User Authentication via Microsoft Azure Active Directory (Federated Authentication) and PlaceOS Calendar Driver, for PlaceOS Room Booking.

If you only require User Authentication you do not need to complete the Calendar Access steps.

If your PlaceOS will have room booking enabled, you will need to complete the Calendar Access steps.

Troubleshooting

Configure PlaceOS for Google Workspace

These steps will help Google Cloud & Google Workspace Administrators make the necessary configurations for PlaceOS User Authentication via Google Workspace (Federated Authentication) and Calendar Visualisation for PlaceOS Room Booking.

If you only require User Authentication you do not need to complete the Calendar Access steps.

If your PlaceOS will have room booking enabled, you will need to complete the Calendar Access steps.

Create Google Cloud OAuth2 Client App

Prerequisites

  • Google Cloud Console Administrative Access or Permissions to Enable API's and create OAuth2 Client ID.

Procedure

If you are just configuring Google User Authentication without Calendar Access, you will need to create a Google Cloud Project for PlaceOS prior to completing this step.

Follow Google instructions to add a new client application to your Google account and obtain the client_id and secret.

Writing Import Scripts

Take a source of data like a spreadsheet and import it into PlaceOS using the REST API.

The advantage of writing a script is that you can update multiple aspects of the system at once, ensure consistency and ideally allow running multiple times as data changes.

These are some example scripts: https://github.com/place-labs/scripted-imports

A simple way of providing data to your scripts that we recommend is by publishing google sheets https://www.youtube.com/watch?v=jxKmnhbrUWs

Analytics

Authentication

PlaceOS Supports a range of authentication providers, both SAML and OAuth2 can be used for authentication.

This section lists configuration for some common authentication providers using SAML.

To configure PlaceOS for Microsoft 365 or Google Authentication, it is best to use OAuth2 detailed in these articles:

  • Configure PlaceOS for Microsoft Azure/365.

  • Configure PlaceOS for Google Workspace.

This section also provides instructions to configure X-API Keys and bearer tokens which can be used for interacting with the PlaceOS API.

365 Room Resources on Azure B2C

As Azure B2C does not explicitly provide us access to end user calendars, or a tenant for room resources you may optionally configure a 365 Tenant to be used for room booking/reservations.

To configure PlaceOS for integration with 365 Calendars, follow the existing instructions for integrating PlaceOS with 365.

Access Control Restrictions are NOT required for 365 Room Resources when using Azure B2C - this steps in the Configuring PlaceOS for Calendar Access on Microsoft 365 may be skipped.

Once Calendar Access has been configured, you will need to configure room resources to accept external meeting requests, this is done via PowerShell:

Set-CalendarProcessing -ProcessExternalMeetingMessages $true

For example:

PS> Set-CalendarProcessing -ProcessExternalMeetingMessages $true

cmdlet Set-CalendarProcessing at command pipeline position 1
Supply values for the following parameters:
Identity: room01_01l6yy.onmicrosoft.com

Configure SAML2 with AD FS

Steps required for enabling SAML2 sign on for PlaceOS on ADFS

Use this page in tandem with Configuring PlaceOS for SAML2 if you are using AD FS. You can follow Microsoft's instructions for most cases. PlaceOS will use these four SAML2 claims:

  • First name

  • Last name

  • Email Address

  • User ID (such as a login name, e.g. UPN or Windows Account Name)

*[AD FS]: Active Directory Federation Services

Configure SAML2 with Google Workspace

Steps required for enabling SAML2 sign on for PlaceOS on Google Workspace

PlaceOS uses a Custom SAML App to provide integrated SSO via Google User Authentication. For detailed steps, see Google's guide on setting up SAML

PlaceOS requires:

  • Organization data

  • Organization path

  • Users full name

  • Email address

  • Unique identifier

You may need to map these as attribute claims.

Configuring PlaceOS

Parameters required from PlaceOS for Configuration:

ACS URL

Assertion Consumer Service. This is where google will send authentication responses with all the claim information.

Example: https://staffapp.placeos/auth/adfs/callback?id=adfs-XXXXX

Entity ID

The Entity ID is the Audience Restriction. It dictates the intended entity or audience for the SAML Assertion. This field is frequently referred to as the "Entity ID" or "Audience URI" by vendors. It can technically be any string of data up to 1024 characters long. Most commonly it's in the form of a URL that contains the Service Provider's name, and is often the same URL as the ACS.

Start URL

This the URL that will start the authentication flow.

Example: https://staffapp.placeos/auth/adfs?id=adfs-XXXXX

Metadata URL

You can provide this URL to Google to help with the setup process.

Example: https://staffapp.placeos/auth/adfs/metadata?id=adfs-XXXXX

Location Services

NOTE:: this is a technical overview, there is a guide on configuring these services in backoffice.

Catering Orders

Catering orders are linked bookings with a type of catering-order. Any driver that operates on bookings with a configurable booking_type can be used for catering bookings.

Examples

Booking Approver

The place/booking_approver driver can be used to auto approve catering orders with this configuration.

approve_booking_types:
	- catering-order

approve_zones:
	- building-zone-id

Booking Notifier

The place/booking_notifier driver can be configured to send notifications for catering orders to a specific email address like this.

booking_type: catering-order
unique_templates: true
notify:
	building-zone-id:
		name: Building Name
		email:
			- [email protected]
		notify_manager: false
		notify_booking_owner: false

Setting notify_booking_owner to true will send the notifications to the booking creator.

To send cancellation notifications a template must be created for the bookings.cancelled_catering-order trigger.

Backend

Backoffice

drivers
system

Limit Application Permissions

In this step, we will now apply the restriction to the PlaceOS Room Booking group, restricting it to only see the calendars required for the purposes of room booking.

The application permissions policy may take up to 12 hours to become available after configuration, during this time PlaceOS will not be able to access room resources.

These steps are taken directly from the Microsoft Documentation: Limiting application permissions to specific exchange online mailboxes.

Prerequisites

  • Exchange Resource Group

  • Exchange Administrator Access

  • Azure App Registration App ID

Procedure

  1. From Exchange Online, or any other service with PowerShell connected to Exchange, open a new PowerShell Terminal.

  2. Run the following command, replacing the arguments for AppID, PolicyScopeGroupID and Description. We will use the AppID and PolicyScopeGroupID from previous steps:

New-ApplicationAccessPolicy -AppId e7e4dbfc-046f-4074-9b3b-2ae8f144f59b -PolicyScopeGroupId [email protected] -AccessRight RestrictAccess -Description "Restrict this app to members of distribution group PlaceOS Room Booking."

3. The application policy should now be applied, this can be tested using the `Test-ApplicationAccessPolicy` cmdlet:

Test-ApplicationAccessPolicy -Identity [email protected] -AppId e7e4dbfc-046-4074-9b3b-2ae8f144f59b

4. If we test using a regular user, the result should be denied:

5. If we test using a room resource address, the result should be granted:

Systems

Systems are collections of modules

A system is the main logical building blocks within PlaceOS. They contain three components:

  • A collection of

  • which will apply to the system and modules in it

  • Basic metadata (name, description etc)

Purpose

Systems often represent physical spaces, such as meeting rooms. They can also represent connected items which run across physical spaces, such as a range of digital signage. Otherwise, they can represent a non-physical system with data inputs and outputs, such as a payment portal.

Systems and Zones

are groups of systems which can reflect their physical or conceptual groupings. Systems can belong to zero or more zones.

Settings Inheritance

Systems inherit settings from their Zones, in the order specified on their Zones tab (highest priority at top).

Zone Settings (in the hierarchical order specified by the System) > System Settings

Zones

Zones are collections of systems

Zones are collections of . A system can have tags marking it as a member of any number of zones.

Purpose

They serve two main purposes:

  1. Logical groupings of systems with common traits, generally:

    • Those in the same physical space, but at a larger scale than systems. For example, if each system is a room then the building would be a zone

    • Systems of the same general concept. For example if you have customer-facing systems zoned separately to internal systems

  2. A point to define that need to affect a set of systems in the same way

Settings Inheritance

Zones do not inherit settings from the Parent zones.

Guides

Crystal

Information and Resources related to the Crystal Programming Language

Crystal is a programming language with the following goals:

  • Have a syntax like Ruby (but compatibility with Ruby is not a goal)

  • Statically type-checked but without having to specify the type of variables or method arguments

  • Be able to call C code by writing bindings to it in Crystal

  • Have compile-time evaluation and generation of code, to avoid boilerplate code

  • Compile to efficient native code

Why Crystal

Security: we can distribute a single static binary in a docker container, there is little chance of exploit.

Speed: the binaries are fast, see .

Developer happiness: all the benefits of Go Lang with the elegance of Ruby.

Resources

Installing Crystal

Crystal Documentation

MQTT

MQTT messaging protocol for IoT

Overview

MQTT is an standard messaging protocol for the Internet of Things (IoT). As a lightweight publish/subscribe messaging transport, it's ideal for connecting remote devices with a small code footprint and minimal network bandwidth.

Its versatility makes it suitable for a wide variety of industries. These include automotive, manufacturing, telecommunications, oil and gas, etc.

MQTT allows for messaging between device-to-cloud and cloud-to-device. This simplifies broadcasting messages to groups of receivers. Further information is available from the

MQTT in PlaceOS

PlaceOS supports publishing state information via MQTT. This provides environment information to external systems such as

MQTT messages consist of a header and a payload and typically have low bandwidth usage. The header declares the topic of the message, and the payload carries data as key-value pairs.

PlaceOS uses two types of message sent over MQTT: State Changes and Metadata. For further information on configuring MQTT for PlaceOS, see the guide on

Resources

OAuth2

OAuth2 Standard Overview and Use with PlaceOS

Overview

OAuth is an open standard for access delegation, commonly used as a way for internet users to grant websites or applications access to their information on other websites but without giving them the passwords.

OAuth provides clients secure delegated access to server resources on behalf of the owner. It specifies a process for resource owners to allow third-party access to their server resources. Most importantly, it allows access without providing credentials.

OAuth works directly with HTTP. It issues access tokens to third-party clients through an authorization server. The resource owner approves which tokens get issued. The third party then uses the token to access the protected resources hosted by the resource server.

OAuth2 in PlaceOS

PlaceOS uses the OAuth Standard (OAuth2) to integrate to third-party services including:

  • Microsoft Graph API

  • Google API

  • Cisco Meraki

OAuth provides PlaceOS access to read and write specific information needed for our application.

The Service Provider (SP) can impose extra permissions and scopes including read/write permissions.

PlaceOS also offers OAuth Access to our Staff and Booking API.

Resources

Step 1: Room Calendar Access

The following steps show how to set up an Azure App Registration to allow PlaceOS to read Exchange Online Room Resource Calendars

The PlaceOS Application requires access to read the booking state of room resources within the Microsoft 365 domain.

This is achieved by creating a with , configuration of an Exchange Group for Room Resources and applying an Application Restriction Policy to the Exchange Group.

Complete the following steps to correctly configure calendar access.

The steps should be completed in the following order:

The steps in the following pages may need to be performed by different platform administrators in your organisation, so be sure to capture the required information to be passed between each step.

  1. Create the Azure App Registration PlaceOS will use to authenticate with the MS Graph API.

  2. Create an exchange calendar group to provide PlaceOS Access with only the required room resource calendars.

  3. Apply an application permission to the Exchange Calendar Group.

  4. Apply the required configuration in PlaceOS to allow the platform to connect and read your calendar resources.

PlaceOS has made the decision to implement our resource calendar visualisation in this method to heavily restrict the application access to organisation calendars.

This method creates a strict separation of concerns between user authentication and access to PlaceOS and the PlaceOS Room Booking Functions, you can think of this as the PlaceOS Daemon.

Using the documented method will prevent the application from reading all calendars in your domain, such as the CEO's calendar and ensure scope is enforced to only provide access to those room resource calendars required for the correct function of PlaceOS.

Create a PlaceOS Authentication Source

To complete this step, ensure you have already followed the

This step can be completed by PlaceOS or your PlaceOS Integration Partner.

Prerequisites

  • PlaceOS Backoffice Administration Access

Procedure

  1. In PlaceOS Backoffice navigate to the Domains tab.

  2. Select the domain you would like to add Microsoft Authentication to.

  3. Click the Authentication Tab.

  4. Click New Auth Source.

  5. Select OAuth as the auth source type.

  6. Provide a name eg. 'Microsoft AD'.

  7. Click Save.

  8. Copy the Auth Source ID eg. oauth_strat-Dw9b-5_lO3

  9. You will require the Auth Source ID to be used as the Azure App Registration Callback URI, for example: `https://placeos-dev.im/auth/oauth2/callback?id=oauth_strat-Dw9b-5_lO3`

Blocked or Blacklisted IP Error

Issue

An issue may arise where users receive an email from Microsoft when attempting to book a room via PlaceOS that states the email was undeliverable to the destination, noting the sender and receiver are both in the same domain.

The specific error:

Root Cause

This is caused by API Spam from the PlaceOS application to the Microsoft 365 via the Graph API Endpoint.

This condition is triggered where PlaceOS drivers are set to a running state against your tenant prior to correct configuration and API access been granted.

PlaceOS drivers related to Microsoft Graph API including the Microsoft Graph API, Staff API, Calendar Driver and Bookings Driver should not be put into an active state until configuration of Microsoft Graph API is complete.

Resolution

  1. Navigate to the Exchange Admin Centre

  2. Click Support

  3. Raise a new Support Request

  4. Advice you have received an error that email is undelivered due to spam blacklist.

  5. A Microsoft agent will contact you and run a diagnostic tool that will resolve the issue typically within an hour.

Google Configuration

To use google APIs you’ll need a server to server OAuth2 application configured. This involves creating a service account that will be used for authentication.

The service account can then “act as” staff in the organisation to perform action on their behalf, such as booking meeting rooms.

See:

There are some actions that regular staff do not have permission to perform, such as:

  • Listing the users in the organisation

  • Interacting directly with resource calendars.

For this functionality a user account should be created that is granted special permissions for these activities.

Configure Access to Google Resource Calendars

To perform actions on resource calendars, like reject meetings, the PlaceOS Service User account needs to have delegated access to these calendars.

Prerequisites

  • Google Workspace Administrative Access

Procedure

  1. Navigate to Calendars

  2. Select the Resources

  3. Select the Room Resource Calendar

  4. Under Share with Specific People add the PlaceOS Service User

  5. Ensure the Service User has 'Make changes to events' permission.

User Authentication

One advantage of using OAuth2 over SAML is that it is possible to require individuals to authorise access to certain resources. Thus users grant access to PlaceOS which can maintain a refresh token for offline access as needed.

Prerequisites

  1. Confirm the final UAT and PROD URLs of the web apps

  2. Ensure that the DNS entries for these URLs are active and forwarding to the server(s)

  3. Ensure that the SSL certificates for the above domains are signed and recognized as secure

User Access Tokens

User tokens obtained from the OAuth2 flow are stored in the database and can be used for making requests on behalf of the users logging in.

You can obtain a token via POST /api/engine/v2/users/resource_token It will return a JSON payload

If the OAuth2 service returned a refresh token then this API will always return a valid token, refreshed as required (there is never direct access to the refresh token)

With multiple authentication sources you may have to specify which source to use for OAuth configuration:

Create a PlaceOS Authentication Source for Google

To complete this step, ensure you have already followed the

This step can be completed by PlaceOS or your PlaceOS Integration Partner.

Prerequisites

  1. Confirm the final UAT and PROD URLs of the web apps

  2. Ensure that the DNS entries for these URLs are active and forwarding to the server(s)

  3. Ensure that the SSL certificates for the above domains are signed and recognized as secure

  4. PlaceOS Backoffice Administration Access

Procedure

  1. In PlaceOS Backoffice navigate to the Domains tab.

  2. Select the domain you would like to add Google Authentication to.

  3. Click the Authentication Tab.

  4. Click New Auth Source.

  5. Select OAuth as the auth source type.

  6. Provide a name eg. 'Google Workspace'.

  7. Click Save.

  8. Copy the Auth Source ID eg. oauth_strat-Dw9b-5_lO3

  9. You will require the Auth Source ID to be used as the Azure App Registration Callback URI, for example: `https://placeos-dev.im/auth/oauth2/callback?id=oauth_strat-Dw9b-5_lO3`

Add User Login Redirects

Once the Authentication Source is configured, we need to ensure PlaceOS Applications redirect the user to the authentication provider to login.

Prerequisites

  • PlaceOS Backoffice Administrator Access

Procedure

  1. Login to PlaceOS Backoffice

  2. Navigate to the Domains tab.

  3. Select the Domain for your organisation.

  4. Click on the Edit icon.

  5. Set the login URL to /auth/login?provider=adfs&id=[ADFS-ID-HERE]&continue={{url}}, replacing the [ADFS-ID-HERE] with the authentication source ID created in '' instructions, leaving the {{url}} as is.

  6. Set the logout URL to /auth/logout?continue=https://sso.org.com/logout if they haven’t provided you a logout.

Debugging

The first step in this process should be to get the raw request.

Often you can see if a request attribute is not lining up to an attribute statement by inspecting the XML.

You can paste the resulting data into this

Then paste the XML into (so it’s readable)

There are two methods of getting SSO data, described below:

  1. If you have an account you can use to test

  2. If the client is logging in and you have access to logs

Self Check

  1. Open the Chrome or Firefox inspection tool

  2. Go to the network tab

  3. Select: preserve log

  4. Go through the login flow

The request coming back to the assertion URL is the one you want to inspect.

Assertion URL: /auth/adfs/callback?id=[ADFS-ID-HERE]

Copy and paste the SAML response into the SAML decoder.

Azure B2C

is an authentication service provided by Microsoft designed to enable any users to register and authenticate against a web application.

Azure B2C may be used with PlaceOS where the organisation wish to provide external access to users not part of their organisation, this is commonly used for shared working or co-working spaces.

Under this configuration, Azure B2C is configured and added to PlaceOS as a OAuth2 Provider.

To use Azure B2C, you will require:

  • An active Azure Tenant

  • An Azure Subscription with valid payment method

  • Administrator Access to an Azure B2C Tenant and/or sufficient access to create and deploy the Azure B2C Tenant from your existing tenant.

These instructions will assume you are able to configure an Azure B2C Tenant under your existing tenant and will only cover the specific requirements for PlaceOS Configuration.

There are two steps to this process:

  1. Create Azure B2C Custom Policy Framework in your Azure B2C Tenant

  2. Configure PlaceOS Authentication Source & Azure App Registration

Configure SAML2 with Auth0

Steps required for enabling SAML2 sign on for PlaceOS with Auth0

Use this page in tandem with if you are using Auth0 domains for SSO. You should follow to a register a single-page-app for SAML SSO using Auth0.

Configuration

  • The Name of the application can be the domain name of your instance of PlaceOS

  • On the Addons tab, enable SAML2 Web App and use as a guide

  • Set the Application Callback URL to match the Assertion URL in PlaceOS, e.g. https://auth/adfs/callback?id=adfs-XXXXXX

  • Paste in the below for Settings:

Testing Internal Builds

Internal builds for all product service images are available on . These publish on every push to our service repos on GitHub and are tagged with the associated branch name.

Access is restricted to the outside world to help prevent accidental use. To authenticate:

  1. with read:packages access only.

  2. Run docker login ghcr.io on your machine. This will prompt you for your GitHub username, and a password. Use the PAT you created above.

Dev builds will now be available by prefixing the image name with ghcr.io. For example, if you would like to test branch foo from core, the image tag is ghcr.io/placeos/core:foo.

  1. edit the .env file in your

  2. replace ${PLACEOS_TAG} with the test branch, i.e. ghcr.io/placeos/core:foo

  3. pull down the internal image docker-compose pull

  4. run the new image docker-compose up -d

These must not be used on external infrastructure, but are suitable for internal testing and experimentation. For external development use, platform nightly builds are available on Docker Hub. Extending the example above, core can be accessed there via placeos/core:nightly. Similarly the latest stable build can be found simply as placeos/core.

Diagnostic information for administrators:

Generating server: SEZPR02MB5759.apcprd02.prod.outlook.com

[email protected]
Remote Server returned '550 5.7.501 Service unavailable. Spam abuse detected from IP range. For more information please go to http://go.microsoft.com/fwlink/?LinkId=526653. S(2017052602) [SI2PR02MB5562.apcprd02.prod.outlook.com]'

Original message headers:

Received: from SEZPR02MB5759.apcprd02.prod.outlook.com
 ([fe80::2859:6314:4b05:dd7b]) by SEZPR02MB5759.apcprd02.prod.outlook.com
 ([fe80::2859:6314:4b05:dd7b%2]) with mapi id 15.20.5395.021; Tue, 5 Jul 2022
 00:37:40 +0000
MIME-Version: 1.0
Content-Type: text/plain
Date: Tue, 5 Jul 2022 00:37:40 +0000
Message-ID:
	<SEZPR02MB5759F90749FCD41572767AE8C3819@SEZPR02MB5759.apcprd02.prod.outlook.com>
Subject: test
Spider-Gazelle
Follow these instructions
Language Reference
Standard library API
Roadmap
OASIS
MQTT Website
module
Amazon MQTT Service
MQTT Integration
MQTT Website
PlaceOS State Source Service
OAuth2 Official Site
OAuth2 RFC
Microsoft App Registration
Application Permissions
Azure App Registration:
Exchange Calendar Group:
Limit Application Permissions:
Configure PlaceOS Calendar Driver:
https://cloud.google.com/iam/docs/creating-managing-service-accounts
{
  "mappings": {
      "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
      "first_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
      "last_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
      "login_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/windowsaccountname"
  }
}
Configuring PlaceOS for SAML2
Auth0 instructions
these steps
ghcr.io
Create a Personal Access Token (PAT)
development environment
{
  # a valid bearer token for the current user
  token:   "1234567",
  # optional unix timestamp (seconds)
  expires: 122345
}
For each domain, specify the authentication strategy to us
Azure B2C (Business to Consumer)
steps to add a domain to PlaceOS.
steps to add a domain to PlaceOS.
Creating a PlaceOS Authentication Source
SAML Decoder
Pretty Print

Azure B2C Custom Policy Framework

This guide provides the steps required to set up a User Journey where users will authenticate with 'local' B2C Accounts. You will need to follow additional Microsoft Documentation if you would like to include Social Sign In on your Azure B2C App.

To use Azure B2C with PlaceOS you will need to configure a Custom Policy Framework, using the existing User Flows provided by Azure B2C is not sufficient for use with OAuth2 as it does not provide a User Info endpoint.

Without a User Info endpoint, PlaceOS is unable to correctly create the user record in our users table.

Create Custom Policy

The first step in configuration is to create a custom policy framework and the supporting application registrations.

Microsoft have prepared extensive documentation to complete this process and we recommend following this documentation to complete this step: Microsoft Azure B2C - Create Custom Policy Framework

To complete this step, you will also require the Custom Policy Provider templates.

Download the Custom Policy Templates from Github or git clone https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack

Add User Info Endpoint

To allow PlaceOS to obtain your users information from Azure B2C via the Graph API, you will need to modify the custom policy to support a User Info endpoint.

Microsoft have prepared extensive documentation to complete this process and we recommend following this documentation to complete this step: Microsoft Azure B2C - Add User Info Endpoint to Custom Policies

Add Custom User Attributes

You may also opt to collect additional data from your users when they sign up to the application, these are referred to as Custom User Attributes.

Custom User Attributes may include additional information such as:

  • Users phone number

  • Address

  • Company

  • Department

This information will be stored against the user record in the Azure B2C Directory and can be claimed by PlaceOS where required.

Microsoft have prepared extensive documentation to add Custom User Attributes to your B2C Custom Policy, we recommend following this documentation to complete this step: Microsoft Azure B2C - Add Custom User Attributes

Password Reset Policy

By default, the self serve password reset user flow is not enabled.

You will need to add a custom user sub-journey to your policy to enable self serve password reset facilities, to do this you can follow this guide by Microsoft: Microsoft Azure B2C - Add Password Reset Journey

Examples

We have provided an example Custom User Policy that includes:

  • Local user sign-up/sign-in i.e. using a email address and password.

  • User Info endpoint enabled.

  • Custom User Attributes added.

You can download our example policies from Github or git clone [email protected]:place-labs/azure-b2c-custom-policy-sample.git

Troubleshooting Backend Failures

This document outlines possible areas of failure and how they impact users

Points of failure

etcd Failure

etcd is used by API and Core to determine the addresses of running Core services. As well as to determine where a module is executing in a cluster.

  • Some drivers may not function as the API won't be able to reach them

  • Core instances won't be able to join the cluster and won't launch drivers

  • Most API requests will continue to work, execute and WebSocket requests will fail

  • Triggers will fail

RethinkDB Failure

RethinkDB holds cluster configuration, primarily used by core during initialization.

  • Most API requests will stop functioning, including authentication

  • Existing WebSocket requests will continue to function

Redis Failure

Redis holds the runtime state of the cluster, such as module metadata and module status

  • Status changes and signals will stop functioning

  • Errors raised in modules may prevent some execute requests from functioning properly

  • Execute APIs and cross driver communication will be effected as module metadata will be unavailable

Elasticsearch failure

  • API index / listing / searching requests will fail - this will be mostly apparent when using Backoffice

  • All other aspects of the system will continue functioning

Driver compilation issue / crash

  • Executes directed at a module running on the driver will fail

Troubleshooting

In the event of a failure, being able to isolate which aspect of the system is not functioning is key to a quick recovery.

  • Can you log-in? If not it's probably a RethinkDB Failure or load balancer issue (check if requests are hitting the services)

  • Does Backoffice list the systems? If not it's probably an Elasticsearch failure

If you can login and see systems:

  • Select a system you can safely use for testing

    • Does the list of modules and module functions load? If not it's probably a Redis Failure

    • Does executing a function work from Backoffice? View the response to see the error:

      • If the error says no core instances then core might be down or unable to connect to etcd

      • If the error says unable to connect to etcd then etcd might be down or the API can't connect to etcd

This should help you identify the cause of most issues.

Enable debug logs

All the services support being toggled to output more verbose logs which might help shed light on an issue.

To toggle log levels you send a signal to the service: kill -USR1 <process_pid>

As you typically cannot shell into the containers, you'll have to run this command from the host. You must be running as root and you can find the process pid by running: ps aux | grep <staff|api|triggers|etc>

modules
Settings
Zones
systems
settings
Add Zone Structure to Backoffice

Create Azure App Registration (Application Permissions)

Prerequisites

  • Microsoft Azure Administrator Access or Permission to Create App Registrations

Procedure

In this step, we will create the application access PlaceOS will use to access the room resource calendars in your organisation via Microsoft Graph API.

After completion of this step, we will apply a policy restriction so the application can only access room resource calendars.

  1. Login to Microsoft Azure Portal.

  2. Navigate to App Registration blade.

  3. Create a new App Registration called PlaceOS Bookings Visualiser

    • Supported account types should be ‘Accounts in this organisational directory only’

    • No redirect URI is required.

  4. Note down the:

    • Application (client) ID as this will be required in the next step and also to be provided to PlaceOS.

    • Directory (tenant) ID this will not be required in the next step but will need to be provided to PlaceOS.

  5. Once created, navigate to Certificates and Secrets.

  6. Create a New Client Secret called ‘PlaceOS Booking Visualiser Secret’ and note down the secret value (you will need to supply this to PlaceOS).

  7. Navigate to 'API Permissions'.

  8. Click 'Add Permission'.

  9. Click 'Microsoft Graph'.

  10. Click 'Application Permissions'.

  11. Add the following permissions: - Calendars.ReadWrite - Group.Read.All - User.Read.All

  12. Click 'Add permissions'.

  13. Click ‘Grant admin consent for xyz’.

  14. Configuration of the Azure App is now complete.

  15. Supply PlaceOS or your integration partner with (be aware that supplying these credentials to PlaceOS prior to configuring the application policy in exchange will allow PlaceOS to see ALL calendars in your organisation):

    • Application ID

    • Tenant ID

    • Secret

Exchange Calendar Group

In this step, we will create a group that will contain the room resources you would like PlaceOS to access. Following this, we will create a restricted access policy applied to the group.

This prevents the PlaceOS Application from reading all calendars in your organisation and negates some security concerns around the applications access to sensitive information such as the CEO’s calendar.

Prerequisites

  • Exchange Administrator Access

Procedure

  1. Navigate to the Exchange Admin Centre.

  2. Click Recipients.

  3. Click Groups.

  4. Click 'Add a group'.

  5. Select 'Mail-enabled Security'.

  6. Click Next.

  7. Name the group 'PlaceOS Room Booking' and optionally enter a group description.

  8. Click Next.

  9. Assign an Exchange Admin or Service User as the group owner. The group owner has no impact on PlaceOS integration and is entirely at the organisation's discretion.

  10. Click Next.

  11. Under Add members you will need to add the room resource calendars you would like PlaceOS to have access to, this can optionally be done later via PowerShell if you have a large number of rooms.

  12. Click Next.

  13. Provide a group email address such as [email protected]

  14. Click Next.

  15. Click Finish.

  16. Note down the group email address as this will be required in the next step.

Add User Login Redirects

Once the Authentication Source is configured, we need to ensure PlaceOS Applications redirect the user to the authentication provider to login.

Prerequisites

  • PlaceOS Backoffice Administrator Access

Procedure

  1. Login to PlaceOS Backoffice

  2. Navigate to the Domains tab.

  3. Select the Domain for your organisation.

  4. Click on the Edit icon.

  5. Set the login URL to /auth/login?provider=[AUTH-TYPE-HERE]&id=[AUTH-ID-HERE]&continue={{url}}, replacing the [AUTH-TYPE-HERE] with one of (adfs, oauth2, ldap) & the [AUTH-ID-HERE] with the authentication source ID created in 'Creating a PlaceOS Authentication Source' instructions, leaving the {{url}} as is.

  6. Set the logout URL to /auth/logout?continue=https://sso.org.com/logout if they haven’t provided you a logout.

Debugging

ADFS:

The first step in this process should be to get the raw request.

Often you can see if a request attribute is not lining up to an attribute statement by inspecting the XML.

You can paste the resulting data into this SAML Decoder

Then paste the XML into Pretty Print (so it’s readable)

There are two methods of getting SSO data, described below:

  1. If you have an account you can use to test

  2. If the client is logging in and you have access to logs

Self Check

  1. Open the Chrome or Firefox inspection tool

  2. Go to the network tab

  3. Select: preserve log

  4. Go through the login flow

The request coming back to the assertion URL is the one you want to inspect.

Assertion URL: /auth/[AUTH-TYPE-HERE]/callback?id=[AUTH-ID-HERE]

ADFS: Copy and paste the SAML response into the SAML decoder.

Concierge Access

This page covers the steps required to configure access for Concierge Users, if required.

PlaceOS recommends managing your Concierge users directly in 365 or Exchange Administration to allow Concierge users to be added and removed as required by the business.

Once changes are made on 365 or Exchange Admin the changes will be available to the user in real time via the PlaceOS Concierge App.

Prerequisites

  • 365 or Exchange Administrator Access

    • Group Creation

    • Resource Management

Procedure

Group Creation

To improve security and allow only users who require access to specific rooms, if you have multiple office locations we recommend creating a security group specific to each office.

You can create a single Concierge security group, noting that all members of this group will be able to see all associated room resources.

  1. Navigate to 365 Admin Centre

  2. In 365 Admin Centre select the Teams & Groups Blade -> Active teams & groups.

  3. In Active teams & Groups select Add Group.

  4. For Group Type select Mail-enabled Security.

  5. Give your group a relevant name such as PlaceOS Concierge + Office

  6. Nominate a user (typically a tenant administrator as the group owner).

  7. In add members, select your Concierge users for that office location.

  8. Nominate a email address for the group.

  9. Click Finish followed by Create Group.

Delegate Room Resources

  1. In 365 Admin Centre select the Resources Blade -> Rooms & Equipment.

  2. Select the room you want to provide concierge access to.

  3. Select Edit Exchange Settings.

  4. Select mailbox delegation.

  5. Under Full Access, add your Concierge Security Group.

  6. Select Save.

  7. Repeat these steps for other rooms as required.

You may also apply the Concierge group to a list of rooms via Powershell: https://learn.microsoft.com/en-us/exchange/recipients-in-exchange-online/manage-resource-mailboxes

Create Google Cloud Project & Enable API

Follow these steps to configure a Google Cloud Project and Enable API for PlaceOS.

Prerequisites

  • Google Cloud Console Administrative Access or Permissions to:

    • Create Projects

    • Enable API's

    • Create OAuth2 Client ID.

Procedure

Configure GCP Project

  1. Goto: https://console.cloud.google.com/

  2. Click the Projects Dropdown

  3. Click New Project in the Project Modal

  4. Name your new project something descriptive eg. 'PlaceOS Integration'

  5. Click Create

Enable API

  1. If not directed, open your new project.

  2. From the navigation menu select 'APIs & Services' then 'Dashboard'.

  3. Select 'ENABLE APIS AND SERVICES'

  4. Search for and enable the Following API:

    • Admin SDK (for staff directory)

    • Google Calendar API

    • Google Drive API (for attachments)

    • Marketplace SDK (not Marketplace API

Configure Google Cloud Service Account

Prerequisites

  • Google Cloud Console Administrative Access or Permissions to:

    • Create OAuth2 Client ID.

Procedure

Create Service Account

  1. From the Menu navigate to APIs & Services then Credentials.

  2. Click Create Credentials

  3. Select Service Account

  4. Create the new service account with a descriptive name eg. PlaceOS Service Account.

  5. Ignore the next steps in the Wizard and click return to return to the list of service accounts.

Create Keys

  1. Click on the PlaceOS Service Account you just created.

  2. Click Add Key

  3. Select Create New Key

  4. Create a JSON key

  5. This will save a JSON file to your computer, this will be required to complete the PlaceOS Configuration Steps.

  6. Once the key is saved, enable Domain Wide Delegation.

  7. Click Save.

Add Google Workplace Permissions

If you want to configure this application for use in a subset of the organisation, then ignore this step and follow the steps to Create a marketplace application

Prerequisites

  • Google Workspace Administrator Access

  • JSON Service Account File generated in Configure Google Service Account step

  • The client_id from the JSON file

Procedure

  1. Go to https://admin.google.com

  2. Navigate to Security

  3. Scroll down to Advanced Settings

  4. Select 'Manage API Client Access'.

  5. Add the following API Scopes to the client_id you extracted earlier.

    1. https://www.googleapis.com/auth/calendar,https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/drive.file

  6. Click Authorise.

  7. Navigate to the Security Tab and select API Controls.

  8. Ensure Internal Applications are Trusted.

https://www.googleapis.com/auth/drive.file allows the application to add attachments to calendar events, such as QR codes. It does not allow for reading or modifying any files not created by the application. i.e. there is no access to company documents or attachments staff have added to events.

Create Google Marketplace App (optional)

This only applies to organizations where a particular region or department will be using the application.

This step is not applicable to most organizations.

Prerequisites

  • Google Cloud Console Administrative Access or Permissions to:

    • Create Marketplace Apps.

  • Marketplace SDK Enabled

Procedure

  1. Navigate to Google Cloud Console

  2. From the navigation menu select API Services then Dashboard

  3. Scroll down and click GSuite Marketplace SDK

  4. Select Configuration

  5. Enter an App Name and Description

  6. Uncheck Individual Install

  7. Upload Icons if required

  8. Terms of Service URL is required, you can set this to your organisations homepage

  9. Enter the Scope URL:

    • https://www.googleapis.com/auth/calendar

    • https://www.googleapis.com/auth/admin.directory.user.readonly

    • https://www.googleapis.com/auth/drive.file

  10. Enable Drive Extension and click 'Configure drive SDK'

  11. Fill in the details and icons for the drive application. This is required to have the application deployed without going through the public marketplace store and onboarding process.

  12. Once done and you're back on the marketplace application form, ensure visibility is set to 'My Domain'

  13. Click 'Save Changes'

  14. You will now need to deploy the Marketplace App to the segment of the organisation that will be using the application, refer to these instructions: https://support.google.com/a/answer/172482?hl=en

Google Workspace Service User (RBAC)

There are some actions that regular staff do not have permission to perform, such as:

  • listing the users in the organisation

  • interacting directly with resource calendars

Prerequisites

  • Google Workspace Administrative Access

Procedure

Create a New User

The new user may sit in a different OU to your regular users for security purposes.

To create a new user, if you are not already familiar you can follow these instructions from Google on Creating a Google Workspace User.

Do not assign a password to this user.

It will never have to log on as it will be used solely by the API application.

Assign Permissions

  1. Select the newly created user from the user list.

  2. Click the Roles and Privileges tab.

  3. Click Edit.

  4. Click create Custom Role.

  5. On the privileges selection screen, under the 'Admin API Privileges' select the following permissions:

    1. Organization Units: Read

    2. Users: Read

    3. Groups: Read

  6. When completed, the role summary should look like:

  7. Assign the role to the account.

Enable Sensor UI

Where sensors are installed, the Sensor Management User Interface provides an interface to plot the sensors on the floor map.

This will enable custom zone capacity and enhanced capacity reporting via the zones.

The Sensor Management User Interface is able to plot the following types of sensors:

Prerequisites

  • Administrator access to your PlaceOS Backoffice

  • A Valid Org, Building and Zone Configuration

Enable the Sensor Extension

The first step is to enable the sensor extension.

  1. In PlaceOS Backoffice Navigate to the Admin Tab

  2. Under PlaceOS Admin select Extensions

  3. Select a Domain and Click Add Extension

  4. Configure the new extension as below:

  • Type: zones

  • Name: Sensors

  • URL: https://editor.place.tech/sensor-map/#/editor/{{map_id}}

  1. Click Add Condition and add the following condition:

  • Condition Field: map_id

  • Operation: truthy

  1. Click Add

The Extension is now enabled.

After completing this step you will need to refresh Backoffice to enable the extension.

Using The Sensor Manager

Once enabled, you can place sensors on the floor maps of Level Zones.

  1. Navigate to Zones and select a zone with a tag of level

  2. In the options bar you will now see the Sensors option

  3. Select the Sensors tab.

You will now be able to select sensors from the list and place them according to their physical location on the map.

Configure a webhook

How to configure a webhook for a trigger or a module that can accept a webhook

Webhooks are defined as a condition of a trigger. Add a new trigger that represents the webhook.

  • Supported methods are the HTTP Verbs that should be accepted by the webhook URI

  • The debounce period can be used to prevent multiple triggers occurring over a short period of time. (in seconds)

New trigger configuration form

Once configured this trigger needs to be applied to a system before it's active.

Add a trigger to a system

Once you've added the trigger, you can obtain the webhook URL by clicking the link icon

Obtaining the webhook URI

The webhook URL

The webhook URL will look something like:

https://my.placeos.domain/api/engine/v2/webhook/trig-DHgkU1~p/notify?secret=kfwu5WYc3a1su

Webhooks that map to module functions

If this webhook is intended to provide data to modules in the system, not just as a condition to execute the trigger actions, then it needs a little more configuration.

  1. in the System trigger list, click the edit button

  2. ensure execute enabled is selected

  3. click save

Enabling webhook to execute module webhook methods

You will have to be aware of the methods that support Webhooks, which might involve looking at the driver code or driver documentation. Examples of webhook methods

  • Meraki Dashboard

  • Rhombus Interop

To build the execute URL you'll need the following information:

  • Module name, i.e. Display

  • Module index, i.e. 1, 2, 3 (Display_2 is index 2)

  • Module method name: i.e. request

Then you can update the Webhook URL from above with this information

/api/engine/v2/webhook/trig-DHgkU1~p/notify?secret=kfwu5WYc3a1suZ&exec=true&mod=Display&index=2&method=request

An alternative form of this URL is (note that the secret is part of the route)

/api/engine/v2/webhook/trig-DHgkU1~p/notify/kfwu5WYc3a1suZ/Display/2/request

This URL can then be used to pass HTTP data to the module and should be provided to the service that uses websockets.

Configuring a default UI

If the user browses to the root of the domain which UI should loa

By default the root URL https://my.domain.com/ will redirect to https://my.domain.com/backoffice/

To override this behaviour:

  1. Goto the domains tab in backoffice

  2. select the domain you would like to configure

  3. add the following config

{
  "default_app": "/concierge/"
}

Bearer tokens

PlaceOS is an OAuth2 authentication service provider. These are a few ways to obtain a bearer token

Bearer tokens are tied to client applications. You can find the list of configured applications in Backoffice on the Domains -> Applications tab.

Password Flow

NOTE:: this flow is not recommended and only works for services accounts, it can be useful to obtain a token simply for testing.

POST /auth/oauth/token

This will return a new token

you can specify more than one scope - defaults to public the scopes selected here must be a subset of those configured on the client application

Making Requests

There are three ways to make an authenticated request with a bearer token:

  1. A HTTP Header: Authorization: Bearer <token>

  2. A URL Param: ?bearer_token=<token>

  3. A HTTP Cookie: bearer_token=<token>

Inspect an existing token

GET /api/engine/v2/api_keys/inspect?bearer_token=yourtoken

GET /api/engine/v2/api_keys/inspect?api-key=yourkey

Authorisation Code Flow

This is the recommended flow for applications built on top of the PlaceOS platform. All PlaceOS templates additionally implement the PKCE extensions for additional security.

  1. First a user session must be established, there are two methods to achieve this

    • Local login, POST /auth/[email protected]&password=developer

    • SSO login, GET /auth/oauth2?id=oauth2-id (generated as part of configuration)

  2. Extract the user cookie (if performing programatically)

  3. Perform the authorisation code flow to obtain a token, with the cookie header set

    • Authorise endpoint: /auth/oauth/authorize

    • Token endpoint: /auth/oauth/token

Configure Endpoint Auto Login

Allow automatic or unatteded authentication for PlaceOS Frontend Applications

PlaceOS requires all interactions to be authenticated and associated with a specific identity. This includes fixed devices such as visitor sign-in kiosks or public information displays. Endpoint auto-login can provide persistent sessions for this style of shared device.

When PlaceOS uses an external Identity Provider with unattended device login, it should use a standard SSO flow. Auto-login provides an option for devices or environments which may not support this.

Prerequisites

  1. Confirm the final UAT and PROD URLs of the web apps

  2. Ensure that the DNS entries for these URLs are active and forwarding to the server(s)

  3. Ensure that the SSL certificates for the above domains are signed and recognized as secure

Step 1: Create a local user account

  1. Login as an admin to Backoffice

  2. On the Users click Add new

  3. Enter the required information, including a descriptive username (e.g. Touchpanel User)

  4. Enter an email address (this does not need to be an active address)

  5. No password required

Step 2: Create an X-API-Key for panel authentication

Follow the , specifying the service account created in the previous step

Step 3: Construct URL for Applications

Most PlaceOS applications can be passed a the API Key as a query parameter, so a destination URL such as:

/booking-panel/#/?panel=sys-F2u1l-oyxJ&x-api-key=be2aba8c8bd2be2a5c719339695b5a63.Edm7STXHDbacbsytz4RGvFc51PnBdQtcU9Yo7BSSVHw

Would be and configured as the continue param:

/auth/login?continue=%2Fbooking-panel%2F%23%2F%3Fpanel%2Fsys-F2u1l-oyxJ%3Fx-api-key%3Dbe2aba8c8bd2be2a5c719339695b5a63.Edm7STXHDbacbsytz4RGvFc51PnBdQtcU9Yo7BSSVHw

The application will use the API key for authentication and will display a bootstrap page for system selection, unless provided additional params that specify a system.

Create Azure App Registration (Delegated Permissions)

Prerequisites

  • OAuth2 Callback URL from PlaceOS Authentication Source

  • Microsoft Azure Administrator Access or App Registration Role

Procedure

  1. Login to Microsoft Azure Portal.

  2. Navigate to App Registration blade.

  3. Create a new App Registration called PlaceOS User Authentication

    • Supported account types should be ‘Accounts in this organisational directory only’

    • See also:

  4. Configure a Web Redirect URI with the PlaceOS Redirect URI created in the previous step eg. https://<YOUR-PLACEOS-DOMAIN>/auth/oauth2/callback?id=<OAUTH_STRAT-XXXX>

  5. Note down the:

    • Application (client) ID as this will be required to be provided to PlaceOS.

    • Directory (tenant) ID as this will be required to be provided to PlaceOS.

  6. Once created, navigate to Certificates and Secrets.

  7. Create a New Client Secret called PlaceOS User Auth Secret and note down the secret value (you will need to supply this to PlaceOS).

  8. Navigate to 'API Permissions'.

  9. Click 'Add Permission'.

  10. Click 'Microsoft Graph'.

  11. Click 'Delegated Permissions'.

  12. Add the following Permissions:

    • Calendars.ReadWrite

    • Calendars.ReadWrite.Shared

    • Group.Read.All

    • User.Read.All

    • offline_access

    • openid

    • profile

  13. Click 'Grant admin consent'

  14. This completes the App Registration.

For more detailed information about the permissions required by PlaceOS, please reference the

Workplace App

Overview of the PlaceOS Workplace App

The Workplace App is the primary user interface for end users to interact with the PlaceOS Platform and facilitates a range of functions, including:

  • Room Booking

  • Desk Booking

  • Locker Booking

  • Car Park Booking

  • Building Floor Plans

  • Real Time Location

  • Room Status

  • Your Bookings

Deployment

The Workplace app is typically configured at the base route of your PlaceOS Deployment i.e. https://yourdomain.placeos.run it may also be accessed via https://yourdomain.placeos.run/workplace

This is configured by the directory of the application configured for the relevant domain per the guide.

Some instances may also have a development interface, pulling from the development branch, this is typically configured as https://yourdomain.placeos.run/workplace-dev

Set The Base Route

If you would like the Workplace App to be the default base route of your deployment, you must configure a default app on the domain.

1. Navigate to PlaceOS Backoffice

2. Click on Domains

3. Select your Domain

4. Click into the Config Metadata

5. Add the default_app parameter

Configure the default_app parameter to the folder for the workplace app, typically /workplace/


{
  "grant_type"    : "password",
  "username"      : "<[email protected]>",
  "password"      : "<password>",
  "client_id"     : "7976...a25be",
  # space seperated scope list
  "scope"         : "public mqtt.read"
}
{
    "access_token": "guxu05o",
    "token_type": "bearer",
    "expires_in": 7200,
    "refresh_token": "CvUwMfso",
    "scope": "public",
    "created_at": 1642051911
}
require "http"
require "uri"

# your client application is expected to know these (public information)
client_id = "797688xxxxxxxxxxx7a25be"
redirect_uri = URI.encode_path_segment "https://localhost:8443/backoffice/oauth-resp.html"
scope = "public"

# NOTE:: in a browser you won't need to handle this manually
auth_cookie = "user=y1GTR1Xf7ZuhoYGYtOuQVyY8hwUhbwnRoh%2FEuuB%2F7frquxI14zpCUqQqJZs%3D--QxluRWU9NngkNOA3--bTqBdZlpiGoOsBtRVV3Tnw%3D%3D"

# make the request
authorize_uri = "https://localhost:8443/auth/oauth/authorize?response_type=code&scope=#{scope}&client_id=#{client_id}&redirect_uri=#{redirect_uri}"
response = HTTP::Client.get authorize_uri, HTTP::Headers{"Cookie" => auth_cookie}

# parse the authorisation code out of the response header
# the location header will be the redirect URI with a code parameter
# example: `https://localhost:8443/backoffice/oauth-resp.html?code=tB2AUQQ8KXm`
location = response.headers["Location"]
code = location.split("code=").last

# Obtain the bearer token
token_uri = "http://localhost:8443/auth/oauth/token?grant_type=authorization_code&code=#{code}&client_id=#{client_id}&redirect_uri=#{redirect_uri}"
token_respose = HTTP::Client.post token_uri

token_respose.body
# => { "access_token":"eyJh", "token_type":"bearer", "expires_in":7200, "refresh_token":"CvUwMfso", "scope":"public", "created_at":1642051911 }
https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#register-an-application
Microsoft Azure Permissions Table.
Paste the PlaceOS Redirect URI into you Azure App Registration

Configure SAML2 with Azure AD

Steps required for enabling SAML2 sign on for PlaceOS on Azure AD

This page will help you if you are using Azure Active Directory for SSO. You will need to configure a new or existing "App Registration" to be the SAML2 identity provider for PlaceOS.

Step 1 - New or Existing App Registration

  1. Login to portal.azure.com and browse to Azure AD > App Registrations

  2. Locate the existing app created for o365 Graph API access. If there isn't one yet, create a new app registration now. You can use this app for both SSO and o365 Graph API access

    • To create a new app registration:

      • Name it and select the appropriate "Support Account types" (typically "Single tenant")

      • Paste the PlaceOS Assertion URL (generated in Step 1 of Configuring PlaceOS for SAML2) into the Reply URL field. Leave the type as "Web". Click Register to finish

    • To configure an existing app registration:

      • Navigate to Overview -> Redirect URIs

      • Paste the PlaceOS Assertion URL (generated in Step 1 of Configuring PlaceOS for SAML2) into the Redirect URI field. Leave the type as "Web". Click Save to finish

  3. Confirm that you have access to the SAML2 Federation Metadata URL for your Azure Tenant. You will need data from this XML file later in Step 3, OR if you configure advanced custom claims. The file URL is generally in the format: https://login.microsoftonline.com/<Tenant_ID_or_Domain_Name>/FederationMetadata/2007-06/FederationMetadata.xml

Step 2 - Edit the App Manifest

In the app Manifest, you need to edit groupMembershipClaims and optionalClaims.

  1. Select the app from Step 1 from the list of App Registrations. Then select Manifest (near the bottom) from the menu on the left

  2. In the editor, set groupMembershipClaims to either “All” or “SecurityGroup”. This page may help you decide which is most suitable for your organization. If unsure, select All. For each option the groups claim will contain:

    • “SecurityGroup” - identifiers of all security groups of which the user is a member

    • “All” - identifiers of all security groups and all distribution lists of which the user is a member

  3. Set the value of the optionalClaims to include first name, last name, UPN, and email in the saml2Token. An example is below:

  "optionalClaims": {
      "idToken": [],
      "accessToken": [],
      "saml2Token": [
          {
              "name": "email",
              "essential": true
          },
          {
              "name": "upn",
              "essential": true
          },
          {
              "name": "family_name",
              "essential": true
          },
          {
              "name": "given_name",
              "essential": true
          }
      ]
  },
  1. Click Save

Step 3 - Collect data required by Backoffice

The App Registration is now configured for PlaceOS. You now need to enter two pieces of information into Backoffice (Step 3 of Configuring PlaceOS for SAML2):

Issuer

You will need the "Application (client) ID" found on the Overview page of your App Registration. Adding spn: to the front will give the "Issuer", e.g. spn:00000000-0000-0000-0000-000000000000. The 0 digits are the "Application (client) ID" from Azure AD. Paste this value into the Issuer field of the SAML2 authentication object you created in PlaceOS.

IDP Target URL

Also known as SAML2 sign-on endpoint. This is the URL that PlaceOS redirects users to, so they can login with your SAML2 ID provider. For Azure AD, the URL is: https://login.microsoftonline.com/<TENANT-ID>/saml2. The "Directory (tenant) ID" can is in the Overview tab of your Azure App Registration. Paste this into the IDP Target URL field of the SAML2 authentication object you created in PlaceOS

X-API-Key guide
encoded
New User Entry
Deploy a Frontend Interface
Step 2 screenshot
Step 3 screenshot
Step 4 screenshot

Configure PlaceOS Calendar Driver

Once the above steps are complete, we can add the required driver on PlaceOS and configure it with the Application ID, Tenant ID and Secret generated in previous steps.

Prerequisites

  • PlaceOS Administrator Access

  • Azure Application ID

  • Azure Tenant ID

  • Azure Application Secret

We recommend secrets are shared using a private service and not transmitted via email, this can include supported parameter stores on cloud services.

Add Calendar Driver

If you have already added the calendar driver, you can skip this step.

  1. In PlaceOS Backoffice navigate to the Drivers tab.

  2. Click New Driver.

  3. Select the Drivers Repository.

  4. Search for Microsoft Graph API

  5. Select the most recent commit.

  6. Click Save.

Configure Calendar Driver

  1. In PlaceOS Backoffice navigate to Systems.

  2. Select the Organisation System, typically this is *Org Name or similar.

  3. Click Modules.

  4. Click Add New Module.

  5. Search for the Microsoft Graph API Driver.

  6. Enter the domain your users will use to access PlaceOS eg. https://yourcompany.placeos.run

  7. Click Save.

  8. Click the Microsoft Graph API Module in the Module list to go to the module settings.

  9. Click the Unencrypted tab.

  10. Enter the following configuration, noting your application ID, tenant ID and secret.

calendar_config:
  tenant: your-tenant-id
  client_id: your-application-id
  client_secret: your-client-secret
  conference_type: teamsForBusiness

Testing Calendar Driver

In order to test the Calendar Driver with Delegated Access for Microsoft 365 you will require at least 1 system configured with a room resource and the Microsoft Graph API Calendar Module and Bookings Module added to the system.

  1. Navigate to a system configured with the correct modules and a valid room resource address.

  2. Under Modules, execute poll_events against the Bookings_1 module.

  3. You should get a successful response.

  4. If you enable Debug and open the Console and the room has valid bookings, you will see the module return the next meeting details.

Add a Domain to PlaceOS

Steps required for adding a domain to PlaceOS

Overview

This guide will step through the process of creating a domain and the associated application(s) that will exist on it.

You must create a domain before adding authentication sources (such as SAML) to it.

Prerequisites

  1. PlaceOS has been deployed

  2. You know the domain(s) required for the deployment and the applications on them

Step 1: Create the New Domain

  1. Log in to Backoffice on the domain created during deployment and select the Domains tab

  2. Select the + button to bring up the New Domain form

  3. Add the following fields:

    • Name: Can be anything to identify the domain, like the domain itself i.e. placeos.domain.com

    • Domain: The actual domain, without a protocol i.e. placeos.domain.com

    • Login URL: The URL redirected to when a user logs in

      • You should usually set this to /login?continue={{url}}

    • Logout URL: The URL redirected to when a user logs out

      • You should usually set this to /auth/logout

Add Domain

Step 2: Add an Application to the Domain

  1. Select the Applications tab

  2. Choose New Application

  3. Add the following fields:

    • Name: Can be anything to identify the applications

      • Usually the folder path where the application resides but capitalized i.e. Backoffice

    • Scopes: Leave this blank

    • Skip Authorizaiton: Check the box to set this to true

    • Login URL: The location that users are redirected to after completing authentication

      • This will generally be something like https://<domain>/<application path>/oauth-resp.html

Add Application

Step 3: Add a User to the Domain

  1. Select the Users tab

  2. Select the New User button

  3. Add or select the following fields:

    • Domain: Select the domain you created in Step 1

    • First Name: Add the first name of the user, this is required

    • Last Name: Add the last name of the user, this is required

    • Email: This will be the username and is required

    • System Admin: Denotes whether the user will be an admin (and thus can access and make changes in Backoffice)

      • This will usually be set to true when creating users in this interface

    • Password and Confirm Password: Enter the password which the user will use to login (when not using SSO)

Add Domain

You can now login with this new user on the domain created.

Deploy a Frontend Interface

How to deploy a frontend interface and add to PlaceOS

CI/CD

To work with PlaceOS frontend repositories must have build artifacts committed to a standalone repository or branch.

The user-interfaces repository already has CI/CD pipelines setup using GitHub Actions.

The user-interfaces repository follows these steps for the build pipeline:

  1. Commit made with changes to libs or an application(apps/<project>)

  2. Pipeline in GitHub Actions starts

  3. Install dependencies and build application(s)

  4. Commit build artifacts to associated branch. For example, a development build of workplace will be committed to build/workplace/dev

Backoffice Setup

Once a build has been created it can be added to Backoffice so that frontends service can pull down the interface.

Add Repository

  1. Navigate to the repositories page

Navigate to the repositories page

2. Add a new repository

Add a new repository
  • Name should describe the UI

  • Folder name will be the path of the UI on the domain e.g. workplace would map to https://my.domain/workplace/

  • Repository is the URL of your git repository with the UI builds

  • Repository type must be Interface for the deployment of a UI

  • Branch is the build branch of the User Interface

Set repository to interface

Configure Routing

  1. You will need to register the new application on your domain.

    • Navigate to Domains

    • Select your application domain + Navigate to the applications tab + Press new Application

    • The login URL should be set to the location of your oauth-resp.html file from ts-client

After that you should be able to access your application at the URL https://my.domain/<folder>/.

Updating

Once the application is setup in Backoffice it should automatically pull any changes to the set branch every hour. If you to make a manual update there is a pull button in the about section of the repositories page.

Updating Interface

Note that if you've set the commit on the repository not to be HEAD the automated pull feature will be turned off and the pull button will do nothing.

Configure PlaceOS Auth Source for Google

Prerequisites

  • PlaceOS BAckoffice Administrator Access

  • client_id and secret obtained from Google.

Procedure

  1. In PlaceOS Backoffice navigate to the Domains tab.

  2. Select the domain you would like to add Microsoft Authentication to.

  3. Click the Authentication Tab.

  4. Identify the OAuth Source previously created.

  5. Click the Edit Icon.

  6. Update missing fields per the table below

Configuring fields

These fields are specific to the OAuth2 provider and tend to differ slightly between providers.

Details on how will be used to describe the following fields

  • name: a friendly name for this authentication configuration

  • client_id: the id provided by the OAuth2 provider when you added a new application

  • client_secret: as above

  • site: the URL of the application requesting access (https://poc.placeos.com in the screenshot above)

  • scope: the scopes, space separated, for the APIs that are intended to be accessed

  • token_method: POST or GET, Google uses a POST to obtain a token

  • authentication_scheme: do we use request params or request body to obtain a token, Google uses the body

  • token_url: the URL to obtain a token from, Googles is https://oauth2.googleapis.com/token

  • authorize_url: this is the URL that initialises the OAuth2 request. .

  • user_profile_url: the is is the URL we can use to test the OAuth2 token and obtain user details

  • info_mappings: this maps PlaceOS fields to User Profile fields

  • authorize_params: query params to pass along with the authorize URL

  • ensure_matching: authorization response fields that should match

Google Example

An example configuration that works with Google

  • scope: profile email

    • https://www.googleapis.com/auth/admin.directory.user.readonly

    • https://www.googleapis.com/auth/admin.directory.group.readonly

    • https://www.googleapis.com/auth/userinfo.email

  • token method: POST

  • Auth Scheme: Request Body

  • Token URL:

  • Authorize URL:

  • User Profile URL:

  • Info Mappings: (PlaceOS -> Google)

    • email -> email

    • first_name -> given_name

    • last_name -> family_name

    • uid -> sub

    • image -> picture

    • access_token -> token

    • refresh_token -> refresh_token

    • expires -> expires

    • expires_at -> expires_at

  • Authorise Params

    • access_type -> offline (this will return a refresh token)

    • prompt -> consent (ensures we are always sent a new refresh token on login)

  • Ensure Matching

    • hd -> my.google.apps.domain (typically the domain after the @ in your login name)

The above stores a refresh token against each user for scoped directory access. A simpler version if token based access isn't required could be:

Deploy AWS Fargate on Nested CloudFormation Stacks

Deployment guide for PlaceOS on Nested AWS CloudFormation templates.

Overview

This page assists with deploying PlaceOS on AWS using CloudFormation templates. The templates configure a PlaceOS Fargate deployment including an optional VPC configuration. The basic premise is:

  1. Upload the nested templates to an S3 bucket

  2. Orchestrate the deployment using a root stack template

You can use the upload-s3.sh script in the AWS command-line tool to upload the required files to a configurable S3 bucket.

A CloudFormation template specifies all the components. Each component is designed to deploy as its own CloudFormation stack.

The root stack requires the following files and directory structure:

  • Security Groups: infra/sec_groups.yml

  • Application Load Balancer: infra/load-balancer-https.yml

  • Elastic File System: infra/EFS.yml

  • Elasticsearch: managed/elasticsearch.yml

  • ElastiCache: managed/elasticache-redis-cluster.yml

  • Fargate Cluster: fargate/ecs-cluster.yml

  • RethinkDB: fargate/rethinkdb/single/rethinkdb-primary.yml

  • etcd: fargate/etcd-service.yml

  • dispatch: fargate/dispatch-service.yml

  • NGINX: fargate/nginx-service.yml

  • Frontends: fargate/frontends-service.yml

  • auth: fargate/auth-service.yml

  • core: fargate/core-service.yml

  • triggers: fargate/triggers-service.yml

  • rubber-soul: fargate/rubber-soul-service.yml

  • REST API: fargate/rest-api-service.yml

  • init: fargate/init-service.yml

VPC Architecture infra/vpc.yml

The VPC root stack template infra/vpc.yml deploys two private and two public subnets. For each of these the user can configure:

  • CIDR ranges

  • An internet gateway

  • Two NAT gateways

  • Routes and route tables

The application load balancer is the only component that should deploy in public subnets.

Configuring the root stack template

Once you have uploaded the files to S3, use root-stack-templates/placeos/deploy.yml to deploy PlaceOS. The required parameters are:

  • BucketName S3 Bucket name where nested templates live

  • CertificateId Certificate Identifier from AWS ACM - required for TLS/SSL

  • EnvironmentName An environment name that is a suffix for resource names

  • PLACEEMAIL Email address to login initially to the application

  • PLACEPASSWORD Password to login initially to Backoffice

  • PLACEUSERNAME Users Name

  • PrivateSubnet1 Select a private subnet

  • PrivateSubnet2 Select another private subnet

  • PublicSubnet1 Select a public subnet

  • PublicSubnet2 Select another public subnet

  • VPC Select the VPC containing the public and private subnets

  • VpcCIDR IP range (CIDR notation) for the VPC

AWS EnvironmentName parameter and Stack naming

The EnvironmentName parameter's uses include:

  • Tagging

  • Service discovery

  • Linking outputs of templates with inputs of later templates

PlaceOS is the default but each deployment in the same VPC should configure its own EnvironmentName. The Stack name you choose for each component has no effect on the function of the deployment.

init: fargate/init-service.yml

initializes the PlaceOS instance and is the final step in the deployment.

This service will never actually finish as the task will exit after it has run. You can update the ECS Service to have zero Number of tasks once it has been successful.

Accessing the deployed PlaceOS Backoffice application

You can expect the deployment to take 20-30 minutes, most of which is Elasticsearch. The Backoffice application will be available at:

https://{Application_Load_Balancer_DNS_NAME}/login?continue=/backoffice

The credentials are the email and password set by the init service. You can also find the application URL listed as an output for the init nested stack.

Calendar Driver

How to Configure the Calendar Driver on PlaceOS

The PlaceOS Calendar Driver connects to Microsoft Graph API (365) or Google Workspace.

The Calendar Driver and the work together to get resource calendar events.

The Calendar Driver can also make ad-hoc bookings from kiosks or room booking panels.

To configure and deploy the Calendar Driver for Microsoft 365 where Delegated Access has been used, please refer to this

Prerequisites

  • Administrator access to your PlaceOS Backoffice

  • PlaceOS Drivers Repository Configured in Backoffice

  • Systems have a valid calendar resource address from Microsoft 365 or Google Workspace

Add Driver

Before we can use the PlaceOS Calendar Driver we must instantiate it as a driver.

  1. Navigate to the Drivers tab

  2. Click the + icon to add a new driver

  3. Select PlaceOS Drivers Repository

  4. Select the drivers > place > calendar.cr Driver Base

  5. Select the latest commit

  6. Click Save

Add to System

You need to instantiate a single instance of the PlaceOS Calendar Driver.

We recommend instantiating the driver in a generic system e.g. *Tracking.

That module is then added to each system that requires booking or calendar functionality.

Instantiate the Driver

  1. Navigate to the system you will instantiate the driver in

  2. Select the Modules tab

  3. Click Add New

  4. Search for the PlaceOS Calendar driver

  5. Click Save

  6. Enable the driver by clicking the black dot, it should turn green

Configure Driver

For Google Integration, we recommend creating a Service User on Google Workspace.

Service Users are the same as a regular Users Accounts with a descriptive name e.g. [email protected]

For testing, you can use your own Google Workspace Account.

The driver is now instantiated as a module, we need to configure the API Credentials.

  1. Navigate to the Drivers tab

  2. Select the Calendar Driver

  3. In the Settings view select unencrypted tab

  4. Delete all the example configuration provided by the driver

  5. If using Google Calendar API, paste this configuration into the encrypted tab:

  6. If using Office 365, paste this configuration into the encrypted tab:

  7. Enter your API information for Microsoft Azure or Google Workspace replacing placeholder text

Add Module to Systems

You can now add the module to other systems as required.

  1. Navigate to an existing system

  2. Select the Modules tab

  3. From the drop down, select the Calendar Module

  4. Click Add Existing

Test Module

You can test by creating a booking or inspecting the state of the Calendar Driver.

Inspecting State should return a valid response.

This should return:

You can also execute a function on the module and view the response.

As a test, executing the list_users function should return a list of users if connected properly.

X-API Keys

PlaceOS can generate API Keys for authenticated access.

The API keys can be used for:

  • Accessing the

  • Using the

Prerequisites

  • Administrator access to your PlaceOS Backoffice

Generate API Key

  1. Login to PlaceOS Backoffice

  2. Navigate to the Admin Tab

  3. Select API Keys

  4. Select the domain the API Key will belong to

  5. Any existing API Keys will be shown in the list

  6. Click Add API Key

  7. Enter the required information:

    • Name: Suitable name for the API Key

    • Description: What the key will be used for (useful for other administrators)

    • Scopes: Select from available scopes (see available scopes below)

    • User: The user in which the API Key will emulate

    • Permissions: Permission level assigned to the API Key (see permission details below)

  8. Click Save

  9. The new API Key will be shown once after it is saved, you will not be able to view it again

Available Scopes

Available Scopes for API Keys are:

  • public A special scope that can access all routes (supports read and write modifiers)

  • api_keys

  • ldap_authentication

  • saml_authentication

  • o_auth_authentication

  • o_auth_applications

  • brokers

  • cluster

  • domains

  • drivers

  • settings

  • modules

  • guests A special scope for guests that provides

  • systems

  • control .read: module class types, function list of a module, module state lookup .write: control websocket, API execute request

  • edges

  • metadata

  • repositories

Available Permissions

  • scope.read

  • scope.write

Using the API Key

API Keys are typically passed in the header of the request, however can be used in the following ways

  1. HTTP Header: X-API-Key: <token>

  2. URL param: ?api-key=<token>

  3. A HTTP Cookie: api-key=<token>

Removing an API Key

  1. Navigate to the API Key Page in Backoffice located in the Admin Tab.

  2. Click the trash icon to remove the key.

Scopes for Common Applications

X-API Keys can be used for unattended panel authentication, scopes are required for these applications to function. The table below outlines common applications that require API Keys and the associated scopes.

Application
Scopes

Location Services

Overview on Location Services Integration on PlaceOS

Location services

Location services are any driver that support the

The driver collects the responses of locatable modules in the same system and returns this as the result of a location search.

Response types

The frontend translates the search responses into a pin on a map. These are the supported formats:

X,Y Location

Such as a location calculated by a wireless network

Map Feature

Such as a desk

Desks highlight based on desk metadata stored against each level zone:

Where bookable is false, desks highlight based on sensor data

NOTE:: no metadata is required for area locations assuming there is a matching map_id, in which case the highlighting is based on the used capacity (low, medium or high)

Booking

Such as an event in a meeting room

Lockers

Any lockers that are in use on the floor

Configuring Cisco Meraki

See the guide.

People Finding with Juniper Mist on PlaceOS

How to locate people using Juniper Mist

Generate a Mist API token

You must record and save the API Token immediately after generating it, as the API Token cannot be re-displayed.

To generate a Mist API Token:

  1. In your browser, access the . The Mist Sign In page opens.

  2. Log in to your Mist account by entering your account credentials.

  3. While logged in to your Mist account, open another browser tab and access the URL

  4. Select POST The generated API Token displays in the following format:

    • "key": "<randomly generated character string>" where the character string is the API Token For example, "key": "34223XFE...e5"

    • also see the

  5. Record and save the API Token, before closing the browser tab.

Note the following about Mist API Tokens:

  • An API Token generated for a specific admin has the same privilege as the user

  • API Tokens are automatically deleted, if not used within 90 days.

Obtain the Site ID

When you configure the PlaceOS connection to Mist, you'll need the site_id for the network you want to monitor. You can obtain these by:

  1. logging into the

  2. select the organisation and site (orange arrow below)

  3. Record and save the UUID from the URL (highlighted in orange)

Configure Mist Websocket Driver

Add the Juniper Mist Websocket driver to the drivers list and configure the settings

Then add the driver to the location services system.

Obtain the list of map ids

We'll need to link Mist maps to PlaceOS maps. Use backoffice to execute the following command:

  • MistWebsocket -> maps (execute)

This will return data like:

Configure Mist Location Services

Add the Juniper Mist Locations driver to the drivers list and configure the settings

Once the floor mappings are in place and the module has been added to your location services system, WIFI users will be searchable.

Configure OAuth2 SSO

Microsoft Azure Active Directory/365

Google Workspace

init
{
  "location": "wireless",
  "coordinates_from": "bottom-left",
  "x": 27.113065326953013,
  "y": 36.85052447328469,
  "lon": 55.27498749637098,
  "lat": 25.20090608906493,
  "mac": "66e0fd1279ce",
  "variance": 4.5194575835650745,
  "last_seen": 1601555879,
  "building": "zone-EmWLJNm0i~6",
  "level": "zone-Epaq-dE1DaH",
  "map_width": 1234.2,
  "map_height": 123.8,

  # driver can include additional data to help with debugging
  "meraki_floor_id": "g_727894289736675",
  "meraki_floor_name": "BUILDING Name - L2"
}
{
  "location": "desk",
  # the count of people at the location, desks will typically be 0 or 1
  # however other location types could have more
  "at_location": 1,
  "map_id": "desk-4-1006",
  "level": "zone_1234",
  "building": "zone_1234",

  # if provided can be used to look up user details
  "mac": "66e0fd1279ce",

  # provided if known
  "capacity": 1
}
[
    {
        "id": "table-07.001.status",
        "name": "Desk 07.001",
        "map_id": "table-07.001.status",
        "bookable": true,
        "features": ["standing"]
    }
    # ...
]
[{
  "location": "meeting",
  "mac": "[email protected]",
  "event_id": "meet-1234567",
  "map_id": "map-1234",
  "sys_id": "sys-id",
  "ends_at": 1234567,
  "private": false,
  "level": "zone-GAgFNk8vm67",
  "building": "zone-GAf3dfZq80~"
},

# Or for a desk or car-space booking etc
# where the asset ID is the map id

{
  "location": "booking",
  "checked_in": false,
  "asset_id": "desk-4-1006"
  "booking_id": "booking-4567",
  "building": "zone_1234",
  "level": "zone_1234",
  "ends_at": 1234567,

  "mac": "placeos-user-id",
  "staff_email": "bob@tmart",
  "staff_name": "Bob Jane"
}]
{
  "location": "locker",
  "bank_id": "bank-id",
  "locker_id": "locker-id",
  "allocated": true,
  "building": "zone_1234",
  "level": "zone_1234",

  # if the locker booking expires
  expires_at: 1234567,

  # something we can use to look up the user, might not be available
  "mac": "bank-id-locker-id-etc"
}
Locatable Interface
Location Service
Cisco Meraki Wireless Tracking
Follow these instructions to configure PlaceOS to use Microsoft Azure Active Directory as the OAuth Provider.
Follow these instructions to configure PlaceOS to use Google Workspace as the OAuth Provider.

Configure PlaceOS Authentication Source

You may supply the client_id and client_secret to PlaceOS or your PlaceOS Integration Partner to complete these steps.

Prerequisites

  • PlaceOS Backoffice Administrator Access

  • Microsoft Azure App Registration client_id and client_secret generated in the Microsoft Azure App Registration steps.

Procedure

  1. In PlaceOS Backoffice navigate to the Domains tab.

  2. Select the domain you would like to add Microsoft Authentication to.

  3. Click the Authentication Tab.

  4. Identify the OAuth Source previously created.

  5. Click the Edit Icon.

  6. Update missing fields per the table below.

These fields are specific to the OAuth2 provider and tend to differ slightly between providers.

Details on how Azure handles OAuth2 will be used to describe the following fields.

PlaceOS Field
Requirement

name

A friendly name for this authentication configuration.

client_id

The Client ID provided by Microsoft Azure App Registration.

client_secret

The Secret created in the Microsoft Azure App Registration.

site

This should be set to: `https://login.microsoftonline.com`

scope

The scopes, space separated, for the APIs that are intended to be accessed

  • openid

  • offline_access

  • calendars.readwrite.shared

  • group.read.all

  • user.read.all

token_method

Azure uses a POST to obtain a token

authentication_scheme

Request Body

token_url

The URL to obtain a token from, Azures is:

https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token

authorize_url

The URL that initialises the OAuth2 request:

https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/authorize

user_profile_url

The URL we can use to test the OAuth2 token and obtain user details:

https://graph.microsoft.com/v1.0/me

info_mappings

This maps PlaceOS fields to User Profile fields (see below).

  • email -> mail,userPrincipalName

  • first_name -> givenName

  • last_name -> surname

  • uid -> id

  • access_token -> token

  • refresh_token -> refresh_token

  • expires -> expires

  • expires_at -> expires_at

PlaceOS Staff API

Once you have completed the above steps, you will also need to create a StaffAPI Record for your domain.

To create the Staff API Record follow these instructions on configuring Staff API.

Driver Dependency Map
calendar_config:
      scopes:      ["https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/admin.directory.user.readonly"],
      domain:      "primary.domain.com", #Your Google Workspace Domain
      sub:         "[email protected]", # User or Service User Account
      issuer:      "[email protected]", # client_id from the JSON
      signing_key: "PEM encoded private key" # private_key from the JSON
calendar_config:
      tenant:          "",
      client_id:       "",
      client_secret:   "",
      conference_type: nil # This can be set to "teamsForBusiness" to add a Teams link to EVERY created Event
{
    "connected": true
}
Bookings Driver
page related to configuring PlaceOS Calendar Driver for 365.
Inspect State
Exec Users

Booking Panel

users.read

systems.read

control

zones.read (optional) `metadata.read` (optional)

Map Kiosk

public.read

Unattended Panel Access
PlaceOS API
Real-time Websocket
access to some APIs
{
  # the api token we generated above
  api_token: "<randomly generated character string>",

  # the id of the site we want to monitor
  site_id:   "site_id"
}
[{
  "id": "cbdb7f0b-3be0-4872-88f9-58790b509c23-j68kows8",
  "name": "Mist Office",
  "type": "image"
}]
{
  floorplan_mappings: {
    "mist_map1_id" => {
      "building":   "zone-12345",
      "level":      "zone-123456",
      
      # free text field for reference
      "level_name": "BUILDING - L1"
    },
    "mist_map2_id" => {
      "building":   "zone-12345",
      "level":      "zone-7891",
      
      # free text field for reference
      "level_name": "BUILDING - L2"
    }
  }
}
Mist management dashboard
https://api.mist.com/api/v1/self/apitokens
mist guide on this process
Mist management dashboard
Org ID (green), Site ID (orange)
Google handles OAuth2
Google details here
https://oauth2.googleapis.com/token
https://accounts.google.com/o/oauth2/auth
https://openidconnect.googleapis.com/v1/userinfo

Backoffice File Upload

Upload files to Backoffice

Overview

Backoffice supports uploading files to cloud storage for secure temporary access or public read. This is useful for device documentation, SVG Floor Maps or Room Photos.

Prerequisites

  1. Configure your storage bucket on the cloud storage platform

  2. Ensure CORS is enabled for the domain Backoffice will be uploading from

  3. Note the storage bucket name

  4. Generate access credentials for the storage bucket

Backoffice configuration

  1. Go to the Manage instance tab and select Upload Storage

  2. You can optionally configure a default storage solution or a per-domain one

  3. Enter the bucket name and access keys, examples below:

adding a storage provider

Cloud configuration

S3 Configuration

You will find this under bucket permissions -> CORS configuration.

[
    {
        "AllowedHeaders": [
            "Authorization",
            "content-type"
        ],
        "AllowedMethods": [
            "GET"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [],
        "MaxAgeSeconds": 3000
    },
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            # add the domains that will be uploading files here
            "https://os.place.tech",
        ],
        "ExposeHeaders": []
    }
]

You will want to configure the access key with minimal permissions too:

  1. In IAM, create a new user to act as the service account

  2. Grant the user no permissions except S3 List, Read, Write, Object permissions management

  3. Generate some access keys to use for signing upload requests

An example IAM user policy for file uploads:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::os.place.tech"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:*Object",
            "Resource": "arn:aws:s3:::os.place.tech/*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "s3:PutObjectVersionAcl",
                "s3:PutObjectAcl"
            ],
            "Resource": "*"
        }
    ]
}

If you don't want the user to have object permission management then you also override the bucket level policy, where all objects are public

{
    "Version": "2008-10-17",
    "Id": "Policy1397632521960",
    "Statement": [
        {
            "Sid": "Stmt1397633323327",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::os.place.tech/*"
        }
    ]
}

Upload Files

Once the above configuration steps are completed, you should be able to upload files to PlaceOS.

To upload a file, drag and drop it into Backoffice in your browser.

The file upload status will be displayed.

File uploads

Once the upload is complete, you can copy the file URL and use this as required.

Map URL

:::tip The uploaded file will be given a discrete URL on the storage bucket, for example: https://s3-ap-southeast-2.amazonaws.com/bucket.name/directory.name/16221818379492128.svg. Characters will be automatically encoded, if present. :::

*[CORS]: Cross-Origin Resource Sharing *[IAM]: Identity & Access Management

Native Booking Panel App

The PlaceOS Native Booking Panel app is designed to be deployed on Apple iPad and Android Tablet devices.

The PlaceOS Native Booking Panel App may be deployed stand-alone from the public Apple App Store and Google Play Store or deployed to a fleet of devices via a Mobile Device Management platform.

The Mobile Device Management platform will provide some added benefits of passing additional pre-configuration to each device to make the set up process easier. End users would need to refer to documentation specific to their MDM for these instructions.

Configuring The App

Configuring the App requires three key pieces of information:

  • PlaceOS API Key: Click here to learn how to generate an API Key.

  • Your PlaceOS URL.

  • A Room Booking System configured.

  1. On first launch, the app will provide some brief instructions detailing the required information as above.

  2. On the second screen, enter your PlaceOS Domain/URL. This is the URL your users use to access existing PlaceOS Apps eg. https://yourcompany.placeos.run

  3. Next, enter the API Key you have generated or was provided to you by PlaceOS Support.

  4. Finally select the room you wish the panel to display. If you have multiple systems a drop down will be shown, or you can search for the system by name or ID.

  5. The app will launch and display the room booking panel interface.

Configuring Device to Remain On

When using an Android Tablet or Apple iPad as a Kiosk Device, you will need to ensure the device will remain unlocked.

If you are using a Mobile Device Management platform you can configure this as part of your room booking profile.

For Apple devices, this is referred to as Guided Access and can be configured with these instructions.

Screen Pinning is one technique on Android devices, although depending on the make and model of the tablet you may require a third party app to configure Kiosk Mode. You can read more about pinning apps here.

Deploying via MDM

The booking panel app is designed to support custom configuration that can be deployed along with the app via a MDM Platform such as SimpleMDM or Google Device Manager.

For this to work, you must use the correct keys in your configuration schema and submit the required information.

Once the data is received by the device, they will display the custom configuration and ask for the data to be validated. Once this is complete, you can set the appropriate Room System on the device.

Dictionary

The following dictionary is required for generating custom configuration:

key
type
Required
example

bundleId

String

iOS: Required Android: Not Required

place.technology.bookingPanel

apiKey

String

Required

2246a9570b1e821a337c47353c.cUm33sGOmjHlSCbf5M07v8y8vRa4_GBmCW7hFkU

domainName

String

Required

SystemId

String

Optional

sys-223fn20n

skipInteractiveSetup

Bool

Optional

"skipInteractiveSetup"="true"

<?xml version="1.0"?>
<managedAppConfiguration>
  <version>1</version>
  <bundleId>place.technology.bookingPanel</bundleId>
  <dict>
    <string apiKey="2246a9570b1e821a337c47353c.cUm33sGOmjHlSCbf5M07v8y8vRa4_GBmCW7hFkU"></string>
    <string domainName="https://placeos-dev.aca.im"></string>
    <string systemId="sys-223fn20n"></string>
  </dict>
</managedAppConfiguration>

ChatGPT / LLM Capabilities

Using drivers to provide capabilities to large language models

It's possible to create a custom LLM chat bot per system, providing the LLM with the ability to execute functions within the system.

Example system configuration

For a system to work as a ChatGPT chat bot it requires the PlaceOS LLM Interface driver. This driver is used to define the system prompt and the initial message to display to the user.

Drivers implementing the ChatFunctions interface expose the available functionality to the LLM.

Building a capability

Each driver should encapsulate related functions that can easily be summarised. This summary is used by the LLM to discover the driver with the function it needs.

The key to building effective functions is to make them as human friendly as possible. If the parameters are hard to understand and not constructed for a human the LLM will produce more errors. An example would be to use parsable date strings over Unix timestamps.

Descriptions should explain how to use any non-obvious parameters and be firm when describing any order of operations:

# Capability example
getter capabilities : String do
  String.build do |str|
    str << "provides details of my daily schedule, meeting room bookings and events I'm attending.\n"
    str << "lookup or search for the email and phone numbers of other staff members if you haven't been provided their details. Do not guess.\n"
    str << "check schedules before booking or moving meetings to ensure no one is busy at that time\n"
  end
end
# Parameter usage example, only functions with descriptions are provided to the LLM
@[Description("search for a staff members phone and email addresses using odata filter queries, don't include `$filter=`, for example: `givenName eq 'mary' or startswith(surname,'smith')`, confrim with the user when there are multiple results, search for both givenName and surname using `or` if there is ambiguity")]
def search_staff_member(filter : String)
  # ...
end

See the example TODO driver and spec as a template.

Configuring access to OpenAI or Azure APIs

Either OpenAI or Azure APIs can be used to provide LLM model functionality. API keys can be defined for each domain in the internal settings.

Settings

"openai": {
  # either the OpenAI or Azure API key
  "api_key": "",
  # specify your Azure API URI here. Don't include key if not in use.
  "api_base": "",
  "api_model": "gpt-4-1106-preview",
  # max tokens before the conversation needs to be truncated
  # this is a good default for GPT 4
  "max_tokens": 11264
}

Chat API

These HTTP and Websocket API routes available for interfacing with the LLM

Websocket

To establish a new chat, open a websocket to: WS /api/engine/v2/chatgpt/chat/:system_id

Request messages are raw strings (frontend -> backend) What do I have on today?

Response messages are JSON encoded (backend -> frontend)

{
  # use the chat_id to recover a chat if the websocket is disconnected
  "chat_id": "chats-GXs4_ynIhe",
  "message": "checking Schedule capabilities",
  # progress messages are provided as the request is worked on
  "type": "progress",
  "function": "list_function_schemas",
  # token usage is provided for debugging purposes
  # as you don't want requests exceeding your limits
  "usage": {
    "prompt_tokens": 883,
    "completion_tokens": 16,
    "total_tokens": 899
  }
}
{
  "chat_id": "chats-GXs4_ynIhe",
  "message": "performing action: Schedule.my_schedule({\"day_offset\" => 0})",
  "type": "progress",
  "function": "call_function",
  "usage": {
    "prompt_tokens": 2197,
    "completion_tokens": 26,
    "total_tokens": 2223
  }
}
{
  "chat_id": "chats-GXs4_ynIhe",
  "message": "condensing progress: Checked schedule for today, no meetings are present.",
  "type": "progress",
  "function": "task_complete",
  "usage": {
    "prompt_tokens": 2240,
    "completion_tokens": 23,
    "total_tokens": 2263
  },
  # progress messages are pruned from the chat to minimize token usage
  "compressed_usage": 883
}
{
  "chat_id": "chats-GXs4_ynIhe",
  "message": "You don't have any meetings scheduled for today. Is there anything else you would like to know or do?",
  "type": "response",
  "usage": {
    "prompt_tokens": 931,
    "completion_tokens": 23,
    "total_tokens": 954
  },
  "compressed_usage": 954
}

If the websocket is dropped you can resume the chat where you left off by re-establishing with the resume parameter: WS /api/engine/v2/chatgpt/chat/:system_id?resume=chats-GXs4_ynIhe

REST API Routes

  • List your past chats GET /api/engine/v2/chatgpt/

  • List the chat history GET /api/engine/v2/chatgpt/:chat_id

  • Delete chat history DELETE /api/engine/v2/chatgpt/:chat_id

MQTT Integration

Guide on integrating PlaceOS with MQTT messaging

PlaceOS supports publishing module state information via MQTT. This provides environment information to external systems such as

MQTT messages consist of a header and a payload and typically have low bandwidth usage. The header declares the topic of the message, and the payload carries data as key-value pairs. PlaceOS uses two types of message sent over MQTT: State Changes and Metadata.

State Change

Changes to module state propagate in real time. All change messages share the following topic structure:

In this structure, each section is a unique identifying tag to represent part of the system.

  • <org>: Organization ID

  • <bld>: ID

  • <lvl>: ID

  • <area>: ID

  • <sys>: ID

  • <drv>: ID

  • <mod>: ID

  • <idx>: Module Index

  • <state>: State Key

On a state change, the module will publish a message with the payload containing the new state value as a JSON entity. The associated driver defines the structure and frequency of this state change.

Some systems may not have a building, level, or area. If they generate a state change, the missing topic level will be an underscore character (_).

State Change Payload

The payload is the value of the status variable paired with a timestamp

Metadata

Metadata is available for building, level, area, system and driver tiers. The format is this persistent topic:

Metadata Payloads

Metadata payloads are JSON objects that contain model info for the publishing entity. This includes the human-readable "friendly name", e.g.

Wildcard Subscriptions

Wildcards can replace any topic level to catch state information across different services. Some common examples are:

All events within a building:

Connected status of all devices:

Power status for all displays:

Call status information for Cisco VC endpoints (dep-123 is the driver ID for Cisco VC):

Privacy

Some deployment requirements may include filtering of sensitive information. The system parses state changes for content such as email addresses or user IDs before they propagate. A match can lead to actions such as:

  • Replacing the value with a hashed version of itself

  • Masking the value

  • Dropping the associated event

Cloud Brokers

PlaceOS can integrate with MQTT Brokers including the major service providers listed below.

*[MQTT]: Message Queuing Telemetry Transport *[VC]: Video Conferencing

Websocket MQTT API

PlaceOS provides an out of the box MQTT solution via the API, delivered over websockets using standard JWTs or X-API-keys for authentication.

Secure WSS available at the route /api/mqtt/ it expects the following MQTT authentication message details:

  • Username: users JWT token or X-API-Key

  • Password: users JWT token or X-API-Key

Example: MQTT Websocket Client ()

Regular users have read and subscribe permissions.

MQTT Clients

We've found the following clients useful

  • JS:

  • Explorer:

Example configuration for explorer

Configure PlaceOS for Azure B2C

Create an Authentication Source for a PlaceOS Domain with Azure B2C.

To allow users to authenticate on PlaceOS provided applications against Azure B2C, we will need to configure a PlaceOS Domain to use Azure B2C as the Authentication Provider.

These steps are similar to configuring OAuth2 for Google Workspace or Azure Active Directory, however, it is important to note the specific endpoints are different and unique to your Azure B2C tenant.

Create an Authentication Source on PlaceOS

  1. In PlaceOS Backoffice navigate to the Domains tab.

  2. Select the domain you would like to add Microsoft B2C Authentication to.

  3. Click the Authentication Tab.

  4. Click New Auth Source.

  5. Select OAuth as the auth source type.

  6. Provide a name eg. 'Microsoft Azure B2C'.

  7. Click Save.

  8. Copy the Auth Source ID eg. oauth_strat-Dw9b-5_lO3

  9. You will require the Auth Source ID to be used as the Azure App Registration Callback URI, for example: https://placeos-dev.im/auth/oauth2/callback?id=oauth_strat-Dw9b-5_lO3

Create Azure B2C App Registration

You will need to create an App Registration for PlaceOS in Azure B2C. This is the application PlaceOS will use to communicate with B2C.

  1. Login to your Azure B2C Tenant.

  2. Under Manage select App Registrations.

  3. Click new App Registration

  4. Give your App a name e.g. PlaceOS User Auth

  5. Under supported account types select: Accounts in this organizational directory only

  6. Under URI Redirect select Web and enter the PlaceOS oauth_strat created in the previous step e.g. https://placeos-dev.im/auth/oauth2/callback?id=oauth_strat-Dw9b-5_lO3

  7. Click Register.

  8. Navigate to API Permissions and confirm offline_access and openid scopes are granted.

  9. Navigate to Certificates and Keys.

  10. Click New Client Secret, name the secret something relevant e.g. PlaceOS Secret.

  11. Note down your Secret Value.

You will need to include this Application ID in the array of Application ID's in the TrustFrameWorkExtension.xml audience section.

Configure the Authentication Source

Information required in this step:

  • App Registration Client ID & Secret

  • Azure B2C Tenant Name

  • Azure B2C Custom Policy Name

In the Authentication Source, enter the following information:

Field
Description
Example

Info Mappings:

PlaceOS
Provider

Add User Login Redirects

  1. Login to PlaceOS Backoffice

  2. Navigate to the Domains tab.

  3. Select the Domain for your organisation.

  4. Click on the Edit icon.

  5. Set the login URL to /auth/login?provider=oauth2&id=[OAUTH_STRAT]&continue={{url}}, replacing the [OAUTH_STRAT] with the authentication source ID created in '' instructions, leaving the {{url}} as is.

  6. Set the logout URL to /auth/logout?continue=https://sso.org.com/logout if they haven’t provided you a logout.

Setup a dev environment

For use when testing, improving or experimenting with PlaceOS on a local machine. Use it for driver, frontend, api and infra development.

Prerequisites

  • if running Windows

Windows

When running on windows ensure docker desktop is linked to your WSL2 shell

Ensure git is using unix line endings. In powershell you can run the following command:

git config --global core.autocrlf false

Set your default terminal in VS Code to your linux shell

  1. Press control -> shift -> P

  2. Type Terminal: Select Default Profile

  3. select your WSL2 terminal from the dropdown

You will then want to ensure there enough resources allocated to the Linux VM

Map a network drive for GUI access

In windows it's better that GUI editors are remotely accessing the linux filesystem, versus linux being mapped to windows as the file permissions will prevent some posix actions.

  1. In Windows Explorer you can browse to \\wsl$\

  2. Then you should see the available Linux environments i.e. \\wsl$\Ubuntu

  3. You can then map this to a drive letter in windows

Once mapped, place all your crystal lang projects in your Linux home folder

Launch VS Code from linux

Edit your code in windows but run extensions on linux (such as your crystal tool formatter)

  1. Install the extension in VS Code

  2. In a linux terminal, browse to the folder with your project files

  3. run the following command: code .

  4. This will launch VS code in windows, however the bulk of operations are occurring in linux

Other minor issues when working with WSL2

Windows doesn't recognise git symbolic links, since the development is occurring primarily in Linux this is safe to ignore: git update-index --assume-unchanged symbolic_link

Configure the partner environment

If you are only looking to develop drivers then you probably don't need the full partner environment and can skip to the next section. Have a look at the then run following commands.

  1. git clone

  2. cd partner-environment

  3. ./placeos start

  4. note the credentials output by the CLI

You can now browse to which is full functional instance of PlaceOS running on your machine.

This is good for testing drivers end to end or connecting to real devices or services.

Developing Drivers

Driver development can be performed with a

  1. git clone

  2. cd drivers

  3. ./harness up (starts the testing UI)

  4. ./harness down (to terminate the docker containers)

Once up you can browse to to start running specs against drivers.

Private / 3rd party repositories

By default the spec runner is looking at public PlaceOS drivers (Feel free to contribute). However you might want to develop some private drivers for a client or manage your own repository. To do this:

  1. In the PlaceOS/drivers repo there is a repositories folder. Clone any 3rd party driver repository in this location.

  2. If you would like to create a new repo for a project, create a fork of our this is a template repository for drivers.

Once the repository is cloned into the repositories folder it will be available in the repository selection dropdown.

Development

Please see the following resources on driver development

It's worth mentioning that drivers are applications in their own right and it's important to update dependencies. Before starting your development, in the repository folder you are working on it's worth running shards update

Native GPT Plugins

For use with OpenAI or Microsoft Copilot

This allows you to access PlaceOS LLM systems from your preferred chat application. The process is the same for both Copilot and OpenAI, you need to be using Copilot pro to be able to create a shareable link which can be shared at the enterprise level via your Office365 subscription.

Prerequisites

Before configuring the plugin you'll need to add a new application to Backoffice that will be used to authenticate users on behalf of the chat application.

  1. Browse to applications in Backoffice and add a New Application

  2. Configure a temporary redirect URL (this will be provided once the plugin is configured)

  3. Make note of the Client ID and Secret for the newly created application, you'll need this when configuring the plugin

Creating a new plugin

OpenAI ChatGPT

  1. Browse to:

  2. Create a GPT

Microsoft Copilot

  1. Follow these instructions to create a GPT:

  2. Follow these instructions to deploy the plugin in your enterprise

Configuring the Plugin

Switch to the configure tab

Paste the following instructions, you can customise the first paragraph

Create new action

Configure Authentication this is where we'll configure the SSO

Configure the following:

  • Authentication Type: OAuth

  • Client ID, Secret that you created earlier

  • Authorization URL: https://<your-placeos-domain>/auth/oauth/authorize

  • Token URL: https://<your-placeos-domain>/auth/oauth/token

  • Scope: plugin

  • Token Exchange Method: POST

Click Save and then configure the following Schema: (make sure to update the system_ids and host)

You'll also need to provide a privacy policy

Final steps

Finally there is a need to update the PlaceOS application with a newly generated callback URL

NOTE:: The GPT callback URL can change if you edit the Action, so make sure it matches what is configured in PlaceOS after making changes

Edit the application on PlaceOS, copying and updating the callback URL. Ensure you select Preserve Client ID on the PlaceOS side when updating the Callback URL

You can now save the GPT and copy the shareable link

Name

A unique name for the Auth Source

Azure B2C

Client ID

The Client ID from your PlaceOS App Registration

Client Secret

The Client Secret from your PlaceOS App Registration

Site

PlaceOS Domain + b2c.com

{placeos_domain}.b2clogin.com

Scope

Scope's the application will use.

openid offline_access

Token Method

POST

Authentication Scheme

Request Body

Token URL

Endpoint for obtaining the token, including your Azure B2C Tenant Name and Policy Name

https://{tenant_name}.b2clogin.com/{tenant_name}.onmicrosoft.com/B2C_1A_SIGNUP_SIGNIN/oauth2/v2.0/token

Authorize URL

Endpoint used for Authenticating Users, including your Azure B2C Tenant Name and Policy Name

https://{tenant_name}.b2clogin.com/{tenant_name}.onmicrosoft.com/B2C_1A_SIGNUP_SIGNIN/oauth2/v2.0/authorize

User Profile URL

The User Info endpoint that was created to pull user information via Graph API

https://{tenant_name}.b2clogin.com{tenant_name}.onmicrosoft.com/B2C_1A_SIGNUP_SIGNIN/openid/v2.0/userinfo

email

sign_in_names.email_address

first_name

given_name

last_name

surname

Creating a PlaceOS Authentication Source
You are an AI assistant in a smart building.
Helping a staff member with every day office related tasks.
When making relative bookings, check you are booking on a business day
business days are Monday, Tuesday, Wednesday, Thursday and Friday
weekends days are Saturday and Sunday, book on these days if explicitly named otherwise confirm if the user meant to book on the weekend

Always attempt to perform tasks before asking the user for additional information. Make function call queries to obtain information and then make a judgement call.

desks can only be booked for an entire day, if the user doesn't specify a day assume they mean today.

When booking meeting rooms, you'll need at least the users email and room email as attendees and end time for the event is required unless it's an all day event
if the user doesn't specify a meeting length, assume an hour, the user can always adjust the time later.
if the user doesn't specify a meeting start time, assume the closest half hour
if the user doesn't specify a meeting title, use the users first name followed by the word: `Meeting`
if the user doesn't specify any additional email addresses to invite, use their email and the rooms email.
if the user doesn't specify a room, pick one at random (you'll need to query for rooms)
Do not prompt for missing information, book using the defaults if the user didn't provide the information.
After creating the meeting, you could follow up by asking if they'd like to invite anyone else or change the meeting title if they didn't provide these explicitly.

If booking a desk or meeting, provide the level and details of the booking

Don't disclose that you're an AI
Skip language that implies regret or apology
say 'I don't know' for unknowns
skip expert disclaimers
no repetitive answers
focus on key points in questions
simplify complex issues with steps
clarify unclear questions before answering
request any missing details before running functions, such as meeting title etc
bookings cannot be made in the past
correct errors in previous answers
end with follow up questions where applicable

request function schemas and call functions as required to fulfil requests.
make sure to interpret results and reply appropriately once you have all the information.
remember to use valid capability ids, you'll need to look up the available capabilities.
you must have a schema for a function before calling it.

if you encounter an error make adjustments and always try again! Check the schema, consider the error message and try again. Don't give up! You can work it out if you give it a few attempts! An empty response is not an error, just the absence of something.
Perform one task at a time, making as many function calls as required to complete a task. Once a task is complete answer the user.

Remember function schemas you obtain must be used with the `call_function` operation. They cannot be called directly.
To use the call_function operation you need to provide the capability id and the function name in the URI
https://chat.openai.com/gpts/mine
https://support.microsoft.com/en-au/topic/create-or-edit-a-copilot-gpt-with-microsoft-copilot-gpt-builder-cbff42b9-25a9-41b7-95ec-93462d04904b
https://www.codetwo.com/admins-blog/microsoft-365-copilot/
https://github.com/PlaceOS/rest-api/blob/master/GPT.yml
https://www.placeos.com/privacy-policy
Add a new application
Configure a New Application
Copy the Client ID and Secret
OpenAI GPT creation page
GPT Configuration
Configure Authentication
Copy the callback URL
Preserve Client ID
https://placeos-dev.aca.im
placeos/<org>/state/<bld>/<lvl>/<area>/<sys>/<drv>/<mod>/<idx>/<state>
{
  "time": unix_integer_milliseconds,
  "value": "payload is a serialized json string"
}
placeos/<org>/metadata/<id>
{
  "name": "Cisco VC"
}
{
  "name": "Level 24"
}
placeos/<org>/state/<bld>/# 
placeos/<org>/state/+/+/+/+/+/+/+/connected 
placeos/<org>/state/+/+/+/+/+/Display/+/power
placeos/<org>/state/+/+/+/+/dep-123/+/+/call_status
Amazon MQTT Service
Building
Level
Area
System
Driver
Module
Cloud MQTT
Google Cloud
MyQTTHub
Heroku CloudMQTT
HiveMQ
Azure
AWS
hivemq.com
https://github.com/mqttjs/MQTT.js
https://mqtt-explorer.com/
MQTT brokers' logos
MQTT Client Configuration
WSL2
GIT installed
Docker installed
Crystal lang
https://docs.microsoft.com/en-us/windows/wsl/wsl-config#example-wslconfig-file
Remote - WSL
readme on the repository
https://github.com/place-labs/partner-environment
https://localhost:8443/backoffice
lightweight spec runner
https://github.com/PlaceOS/drivers
http://localhost:8085/
private-drivers repo
Writing a driver
Writing a spec
Docker WSL2 integration
Map a network drive
Developing drivers

Bookings Driver

How to Configure the Bookings Driver on PlaceOS

The primary purpose of the Bookings Driver is to enable room booking panels, kiosks and space maps.

Best practice is to add the Bookings Driver to every system on PlaceOS that supports room booking.

Prerequisites

  • Administrator access to your PlaceOS Backoffice

  • PlaceOS Drivers Repository Configured in Backoffice

  • Systems have a valid calendar resource address from Microsoft 365 or Google Workspace

Add Driver

Before we can use the PlaceOS Bookings Driver we must instantiate it as a driver.

  1. Navigate to the Drivers tab

  2. Click the + icon to add a new driver

  3. Select PlaceOS Drivers Repository

  4. Select the drivers > place > bookings.cr Driver Base

  5. Select the latest commit

  6. Click Save

Add to Systems

You need to instantiate a single instance of the PlaceOS Bookings Driver in each system that has a bookable space.

  1. Navigate to a bookable system

  2. Select the Modules tab

  3. Click Add New

  4. Select PlaceOS Bookings

  5. Click Save

  6. Click the Black Dot next to the Module to start it

Test Module

You can test by creating a booking and inspecting the state of the Bookings Driver.

Inspecting the state will return a JSON response of all bookings for that systems calendar resource.

In the example below, you will see a positive response with a calendar booking.

Bookings State

Status Variables

Status
Values
Function

booked

(Bool)

true when there is a current (start time < current time < end time)

next_pending

(Bool)

true from pending_before mins before an event start time until the event start time OR until checkin / start_meeting is executed.

current_pending

(Bool)

true from the event start time until pending_period mins after the event start time OR until checkin / start_meeting is executed.

pending

(Bool)

true when either current_pending or next_pending is true

in_use

(Bool)

true when booked AND NOT pending (means that the current event has been checked in via checkin OR start_meeting functions)

status

free, pending, busy

Describes the current status of the room

Settings

List of settings can be found in the driver source: https://github.com/PlaceOS/drivers/blob/master/drivers/place/bookings.cr#L12

Setting
Default Value

pending_before

5 (mins)

Number of minutes BEFORE the Booking start time until the status changes from free to pending

pending_period

5 (mins)

Number of minutes AFTER the Booking start time until status changes from pending to free

disable_end_meeting

false

Exposes a disable_end_meeting status variable such that frontends like PlaceOS template Bookings can detect it and enable/disable it's auto event cancellation functionality (frontend will exec end_meeting causing the current event to be truncated to the current time, freeing up the room (in case of no shows).

Example config

pending_before: 5,
pending_period: 0,
disable_end_meeting: false,

This will make status change to pending 5 mins before the booking start time. Then change to busy at the booking start time.

People Finding with Cisco Meraki on PlaceOS

How to locate people using Cisco Meraki

Wireless

Cisco Meraki provides two interfaces for locating users:

  1. Dashboard API

    • PlaceOS uses version 1 of the API

  2. Scanning API

    • Version 3 of the API is required

Data Collected

Ideally you collect the following:

  1. Username to MAC address mappings

    • Provided by the Dashboard API

    • Regularly polled via HTTP to learn who owns the devices on the network

  2. MAC address to x, y map coordinates and SSID

    • Provided by the Scanning API

    • Cisco Meraki uses a webhook to post data to PlaceOS

    • PlaceOS must be accessible on the public internet with valid TLS certificates

Device Certificates

If devices authenticate against the network using certificates, the username is unknown to Cisco Meraki. In this case, you need to determine usernames another way.

  1. Username to IP address

    • You may have an established method for this process, which you may use

    • One of the most reliable methods is parsing Domain Controller authentication logs

  2. IP address to MAC address mappings

    • Provided by the Dashboard API

    • Regularly polled to learn who owns the devices on the network

  3. MAC address to x, y map coordinates and SSID

    • Provided by the Scanning API

    • Cisco Meraki uses a webhook to post data to PlaceOS

    • PlaceOS must be accessible on the public internet with valid TLS certificates

If you use device certificates, you will need to have Step 1 in near-real-time to match usernames to MAC addresses

Integration Requirements

  1. Cisco Meraki must be on Firmware R26 or higher

  2. Cisco Meraki must be able to connect to PlaceOS rest-api microservice

  3. PlaceOS microservices must be able to connect to Cisco Meraki

  4. Cisco Meraki Scanning API v3 or higher must be configured

    • Provide PlaceOS integrator the validator code

    • Provide PlaceOS integrator the secret code

  5. Cisco Meraki Dashboard API v1 or higher must be configured

    • Provide PlaceOS integrator with API Key

  6. PlaceOS integrator to provide a webhook for posting logs

    • Example script

Wired

For locating users plugged into a wired network, possibly via a docking station.

  1. Dashboard API

    • PlaceOS uses version 1 of the API

  2. SNMP configuration

    • PlaceOS requires Link Up and Link Down traps

    • PlaceOS requires SNMP access to switches directly

Data Collected

  1. Username to IP address

    • You may have an established method for this process, which you may use

    • One of the most reliable methods is parsing Domain Controller authentication logs

  2. IP address to MAC address mappings

    • Provided by the Dashboard API

    • Regularly polled to learn who owns the devices on the network

  3. MAC address to Switch Port mappings

    • Provided by polling the Network Switches (SNMP queries)

    • SNMP Traps allow you to detect changes in real time

    • PlaceOS must be able to receive traps originating from the public internet

Integration Requirements

  1. Cisco Meraki must be able to connect to PlaceOS

  2. PlaceOS must be able to connect to Cisco Meraki

  3. Cisco Meraki Dashboard API must be configured

    • Provide PlaceOS with API Key

  4. Cisco Meraki SNMP access must be configured

    • SNMP Link Up and Link Down traps

    • Direct switch SNMP polling configured

    • Provide PlaceOS with community strings and credentials if using SNMPv3

  5. Provide PlaceOS with list of Switch IP addresses

  6. Provide PlaceOS with Switch Port to Desk ID mappings

Booking Panel App

This page contains common configuration to modify the behaviour of the PlaceOS Web Based Booking Panel App.

In a standard deployment, the booking panel app will be deployed at https://yourdomain.com/booking-panel/

Available Configuration Parameters

The below table lists the available configuration paramters and where they are configured, full details are included below.

Parameter
Type
Config Location
Behaviour

disable_book_now

bool

System or Zone

Enables the Touch to book function on the booking panel UI.

hide_meeting_title

bool

System or Zone

Hides the Title of the Meeting

hide_meeting_details

bool

System or Zone

Hides the Details of the Meeting

show_qr_code

bool

System or Zone

Hide the QR Code in the UI

room_image

string

System or Zone

Show an image in the top half of the panel UI

Enable Touch to Book

Enabling Touch to Book provides a button on the user interface that allows users to make ad-hoc room bookings from the booking panel.

Touch to Book will not work where PlaceOS is integrated to Microsoft 365 using Delegated Access. Touch to Book will only function where Application Access is used.

Booking Panel showing the Touch to Book button enabled.

This feature is enabled by adding configuration to modify the PlaceOS Bookings Driver.

  1. Navigate to Zones.

  2. Select the appropriate zone in the zone hierarchy of which you want to enable this feature.

    • You may enable this feature globally by adding the configuration to the ORG Zone.

    • You may enable this feature to a building by adding the configuration to a BUILDING Zone.

    • You may enable this feature for a single level by adding the configuration to a LEVEL Zone.

    • You may enable this feature for a region by adding the configuration to a REGION Zone.

  3. Select the Unencrypted Tab.

  4. Enter Configuration: disable_book_now: false

    • Alternatively, adding disable_book_now: true or omitting this configuration will hide the touch to book bar.

  5. Save.

  6. Check a Booking Panel to confirm the configuration was set.

Disable Meeting Title

This setting will disable the meeting title from being displayed on the booking panel.

  1. Navigate to Zones.

  2. Select the appropriate zone in the zone hierarchy of which you want to enable this feature.

    • You may enable this feature globally by adding the configuration to the ORG Zone.

    • You may enable this feature to a building by adding the configuration to a BUILDING Zone.

    • You may enable this feature for a single level by adding the configuration to a LEVEL Zone.

    • You may enable this feature for a region by adding the configuration to a REGION Zone.

  3. Select the Unencrypted Tab.

  4. Enter configuration: hide_meeting_title: true

    1. Alternatively the configuration hide_meeting_title: false will show the meeting title, this is also the default behaviour and does not require configuration.

  5. Save

Disable Meeting Details

This setting will disable the meeting details from being displayed on the booking panel.

  1. Navigate to Zones.

  2. Select the appropriate zone in the zone hierarchy of which you want to enable this feature.

    • You may enable this feature globally by adding the configuration to the ORG Zone.

    • You may enable this feature to a building by adding the configuration to a BUILDING Zone.

    • You may enable this feature for a single level by adding the configuration to a LEVEL Zone.

    • You may enable this feature for a region by adding the configuration to a REGION Zone.

  3. Select the Unencrypted Tab.

  4. Enter configuration: hide_meeting_details: true

    1. Alternatively the configuration hide_meeting_details: false will show the meeting title, this is also the default behaviour and does not require configuration.

  5. Save

Disable QR Code

The QR Code can be used by users to access a room features menu that may include:

  • Ad-hoc booking ability

  • Future booking ability

  • AV Control

  • Lights and Blind Control

  • Heating/Cooling Control

The available options in this UI will depend on integrations available for the selected space.

However, you may wish to disable the QR Code entirely from view.

  1. Navigate to Zones.

  2. Select the appropriate zone in the zone hierarchy of which you want to enable this feature.

    • You may enable this feature globally by adding the configuration to the ORG Zone.

    • You may enable this feature to a building by adding the configuration to a BUILDING Zone.

    • You may enable this feature for a single level by adding the configuration to a LEVEL Zone.

    • You may enable this feature for a region by adding the configuration to a REGION Zone.

  3. Select the Unencrypted Tab.

  4. Enter configuration: show_qr_code: false

    1. Alternatively the configuration show_qr_code: true will show the meeting title, this is also the default behaviour and does not require configuration.

  5. Save

Background Image

You may decide to show a image on the booking panel, this may be of the room itself, the building or other relevant image to your organisation.

  1. Navigate to Zones.

  2. Select the appropriate zone in the zone hierarchy of which you want to enable this feature.

    • You may enable this feature globally by adding the configuration to the ORG Zone.

    • You may enable this feature to a building by adding the configuration to a BUILDING Zone.

    • You may enable this feature for a single level by adding the configuration to a LEVEL Zone.

    • You may enable this feature for a region by adding the configuration to a REGION Zone.

  3. Select the Unencrypted Tab.

  4. Enter configuration: room_image: absolute_url_to_image

  5. Save

Enabling Ms / Google Push events

This allows the panel to update as soon as bookings are modified. Reducing reliance on polling.

# use this for MS events
push_notification_url: 'https://yourdomain.placeos.run/api/engine/v2/notifications/office365'

# use this for Google events
push_notification_url: 'https://yourdomain.placeos.run/api/engine/v2/notifications/google'

Once configured, the panels will register for push events and maintain the subscription

Configure SAML SSO

Steps required for enabling SAML single sign on for users logging in to PlaceOS web apps

By default, PlaceOS uses local authentication. An admin account is generated upon initial deployment. The administrator can manually create user accounts in Backoffice (on the Users tab). We recommend switching to federated authentication.

Prerequisites

  1. Confirm the final UAT and PROD URLs of the web apps

  2. Ensure that the DNS entries for these URLs are active and forwarding to the server(s)

  3. Ensure that the SSL certificates for the above domains are signed and recognized as secure

Step 1: Adding Authentication

  1. Login as an admin to Backoffice

  2. On the Domains tab, select the Domain that represents the URL where you wish to enable SAML

  3. In the Authentication section click Add new

This will open up the SAML form. Here is a description of each field:

  • Name: generic field for identifying the SSO

  • Issuer (Identifier / Entity ID): this is what the SSO will use to identify this SSO integration

    • Can be anything, typically use the DNS entry i.e. staffapp.company.com

    • Azure will be in the format: spn:**client-id** - "Application (client) ID" can be found on the Overview page of your App Registration

    • OKTA will need the Issuer to be configured in the OKTA Application to match the value set here

  • IDP target URL: This is the URL where SSO will occur - the login page

    • You can often guess it, but you can set it to any valid URL and change it later

    • Request this URL when sending the metadata URL

    • Azure AD URLs are often in the format: https://login.microsoftonline.com/**tenant-ID**/saml2

    • OKTA URLs are often in the format: https://**okta.domain.com**/app/**app-name**/**app-id-number**/sso/saml

    • AD FS URLs are often in the format: https://adfs.myorganistaion.com/adfs/ls

    • Auth0 URLs are often in the format: https://myorganisation.auth0.com/samlp/

  • Name Identifier Format: the format of the response data

    • Set to urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified

  • Request Attributes: these are the Active Directory fields requested to be sent to us

    • Typically leave this blank on first save, and it will fill in the default value

    • Clients sometimes request you change these to match their system

    • Example:

  • Assertion URL (Reply URL / Callback URL): the PlaceOS URL that SSO sends data back to - to log someone in

    • First set this to the base domain of the application. After saving this configuration for the first time, it will generate an ID (adfs_strat-XXXXXX)

    • See the image below and use the generated unique ID to build the Assertion URL

  • Certificate Fingerprint / Full Certificate: used for verifying a signed login request

    • Not required until after SSO configuration on the client-side

  • UUID Attribute: allows you to override the default unique ID returned by SSO to one of the requested attributes

  • Attribute Statements: This maps requested attributes to database fields

    • Typically leave this blank on first save, and it will fill in the default value

    • Example:

    Name
    Mappings

Once you click save, it will generate an authentication ID. You can find it in the /saml_auths response on the Authentication tab.

URL configuration

  1. Set the following fields to the corresponding URLs, replacing adfs-XXXXX with the generated ID:

    • Assertion URL (Reply URL / Callback URL): https://staffapp.placeos/auth/adfs/callback?id=adfs-XXXXX

    • Metadata URL: https://staffapp.placeos/auth/adfs/metadata?id=adfs-XXXXX

    • Login URL: https://staffapp.placeos/auth/adfs?id=adfs-XXXXX

  2. Edit the authentication and update the Assertion URL with the one you have created above

  3. Email the client with:

    • The Metadata URL so they can configure their systems

    • A request for their IDP target URL

    • A request for their logout URL if they have one (otherwise can redirect to company home page etc)

    • PlaceOS supports signed SSO but not encrypted. SSL transport means the SSO response payload is already encrypted

Once the client has configured their side, they’ll often ask you to change some information. This could be the Issuer, or some request attributes.

Step 2: Register a new service in your authentication provider

You will need to configure your SAML Identity provider dashboard. From the above steps you will need:

  • The Issuer (also known as the Identifier)

  • The Assertion URL (also known as the Callback URL)

  • The Login URL which is the homepage of the app

  • The Metadata URL (optional)

This XML file contains the above information and can be fed into to some configuration dashboards (like AD FS).

This process will vary by provider, see the below guides for common options:

Step 3: Configure default redirects for the PlaceOS Domain

Once you have tested the Login URL above you can update the default login page for the domain.

  1. Click the edit icon for the Domain (above the authentication tab)

  2. Set the login URL to /auth/login?provider=adfs&id=[ADFS-ID-HERE]&continue={{url}}, replacing the [ADFS-ID-HERE] and leaving the {{url}} as is

  3. Set the logout URL to /auth/logout?continue=https://sso.org.com/logout if they haven’t provided you a logout

Debugging

The first step in this process should be to get the raw request. Often you can see if a request attribute is not lining up to an attribute statement by inspecting the XML.

You can paste the resulting data into this

Then paste the XML into (so it’s readable)

There are two methods of getting SSO data, described below:

  1. If you have an account you can use to test

  2. If the client is logging in and you have access to logs

Self Check

  1. Open the Chrome or Firefox inspection tool

  2. Go to the network tab

  3. Select: preserve log

  4. Go through the login flow

The request coming back to the assertion URL is the one you want to inspect.

Assertion URL: /auth/adfs/callback?id=[ADFS-ID-HERE]

Copy and paste the SAML response into the SAML decoder.

Docker logs

Look for the text "Callback phase initiated" and the SAML response data is nearby.

Import Bookable Rooms

Steps required to import existing rooms into PlaceOS

The PlaceOS room bookings module performs the following roles:

  • A cache of upcoming bookings for staff apps

  • Data for real-time analytics (room booked, in-use, empty, etc)

  • Data for room booking panel displays

  • Data for maps

A typical configuration consists of:

  • A system with a resource email addresses defined

  • A unique instance of the PlaceOS Bookings module for that room (logic module)

  • A shared instance of the PlaceOS Calendar module, for making requests to the calendaring system

The script below runs through the level zones and updates any systems with a resource email address to the standard configuration.

Prerequisites

The client ID and secret from the domains tab in Backoffice. You can use any valid administration account credentials for that domain.

  • Create a calendar module that will be shared between all systems, note the module ID

  • Add the Bookings driver to the system and note the driver ID

Room bookings import script

The only dependency is the PlaceOS crystal client.

The script below supports redistribution and customization via the command-line options. You can also update the defaults and run it without any options.

SVG Map Creation

Create SVG Maps for use with PlaceOS

Intro

Maps are where function meets form, and are the starting point of any conversation between a user and the platform.

A bespoke map is like a tailored suit or custom jewellery piece. You can elevate something plain by perfecting the details which takes the final product to the next level. The choices you make along the way will define the story you tell with the finished product. This guide will step you through the process in creating a map that is usable and visually effective.

When going custom you’ll get to choose from a range of details, such as linings, button colours and stitching choices. This is how PlaceOS designs maps for our clients - we take your average engineering map and we give it a makeover to impress the user.

We have also produced a video overview to walk through creating SVG Maps. You can view the video by .

Step 1 - Get Floor Plans

Get architectural floor plan from client and branding colours (if provided).

Step 2 - File Set Up & Import Architecture Drawing To Adobe Illustrator

When working in illustrator it's important to set your art-boards to pixels (px) for web form.

  • Drag drop a JPEG version of the floor plan into Illustrator

  • To get started with the right settings, navigate to Illustrator > Preferences > General

  • Select "scale corners" and "scale strokes and effects". This will ensures all your objects will scale to the stroke you set, which provides freedom in scalability

  • Select "Units" from the side options and double check everything is set to pixels as we are working in web

Step 3 - Creating Your Layers

Before getting started on design it's important to name and order your layers. The ideal way to create layers is to start from the bottom working your way up. For a floor plan that could mean:

  1. Main features such as the skeleton shape of a building

  2. Signage such as toilets, stairs, lifts

  3. Adding furniture and room statuses

Original floor plan should be locked to avoid it moving out of place while designing.

Step 4 - Creating Floors

In a new layer start outlining your architectural floor plan. Fill in the shape with color (as outlined by brand guidelines). Lock the layer and name layer “outline”

Step 5 - Floor Dimension

Copy and paste outline and fill in with color (new layer) and lock the layer - name layer bkd. To give a shadow or building structure effect to the “outline” layer, offset this layer from it. Shift this shape sideways or downward (building shapes vary).

Step 6 - Outlining Floors aka "The Skeleton"

Note that the floor plan walls and outlines do not need the exact thickness of each wall illustrated. The plan needs to define wall areas that are entrances to spaces and rooms. The outline shape of the floor should be a little bit thicker in point size compared to its inner walls to define boundaries.

To get started on the skeleton, select the "pen tool" and select a point size to outline walls. This can be anywhere from 2px-6px. Begin illustrating the custom map, ignoring any outlines that are not needed. For example, pipes near bathrooms or electrical rooms can have a darker blank color fill.

Step 7 - Space Highlights

Next, check the architectural floor plan to see which spaces need signage e.g. rest rooms, stairs or cafe spots. These spaces can have a filled space highlight that compliments the background color of the map.

Step 8 - Compare Floor Plans

Check the architectural floor plan to verify each room. Begin placing furniture and other features in designated spaces and rooms on your map. You may need to create your own designs according to the brand guidelines.

Step 9 - Completing Layers

Make sure furniture and other elements such as plants are proportional to the room size and layout. Ensure plants are block colors and are not composed of a gradient. Once complete, lock the layer and name it “plants and furniture”.

Step 10 - Adding Bookable Rooms

In a new layer, use the “rectangle tool” to create an overlay for room bookings (bookable rooms). Ensure the layer is between 40-60% black. This layer should be on a layer below lines (for appearances to look neater). Name layer “room bookings”.

Step 11 - Booking IDs

In the layer “room bookings” you will need to write room IDs. Clients may give room ID specifications, such as capitalized vs lower case, order of room/level number, etc. Generally, room labels should be in the following format: area-[level].[room number]-status.

All IDs MUST start with a letter (i.e. must not start with a number or other character). This is due to the CSS selector conventions.

Step 12 - Adding Text

Once you have labeled each room layer, you can create another layer “text”. On it, you will label each room and section of the map. This text should be Arial and anything larger than 6pt text size. For rooms use Arial bold or medium for other sections of the map use Arial regular or light. Make sure text is a color that stands out against the map background. You can check that this is accessible with a .

Step 13 - Adding Icons

On the final new layer, add icons to your map. Icons should follow the branding guidelines e.g. style, colors, line weight/thickness etc. Ensure colors chosen for icons also stand out against the map background.

Step 14 - Checking Names/ID

Once all elements are on your map go back to the booking layer and be sure to check the name and number of each room. We recommend placing the names in order, to ensure there are no double ups. Remember the SVG map will not work on the frontend if two rooms have the same ID.

:::tip IMPORTANT All IDs MUST start with a letter (i.e. must not start with a number or other character). This is due to the CSS selector conventions. :::

Step 15 - Exporting

Go to File > Export As > SVG > Save to designated folder.

Ensure the following is correct:

  1. Styling > Internal CSS

  2. Font > SVG

  3. Images > preserve

  4. Object IDs > Layer names Decimal > 3

  5. Tick "minify" and "responsive"

  6. Select "OK"

Checklist

Testing drivers

There are three kind of drivers

  • Streaming IO (TCP, SSH, UDP, Multicast, ect)

  • HTTP Client

  • Logic

From a driver code structure standpoint there is no difference between these types.

  • The same driver can be used over a TCP, UDP or SSH transport.

  • All drivers support HTTP methods if a URI endpoint is defined.

  • If a driver is associated with a System then it has access to logic helpers

During a test, the loaded module is loaded with a TCP transport, HTTP enabled and logic module capabilities. This allows for testing the full capabilities of any driver.

The driver is launched as it would be in production.

Code documentation

For detailed automatically generated documentation please see the:

Expectations

Specs have access to Crystal lang . This allows you to confirm expectations.

There is a good overview on on the crystal lang docs site

Status

Expectations are primarily there to test the state of the module.

  • You can access state via the status helper: status[:state_name]

  • Then you can check it an expected value: status[:state_name].should eq(14)

Testing Streaming IO

The following functions are available for testing streaming IO:

  • transmit(data) -> transmits the object to the module over the streaming IO interface

  • responds(data) -> alias for transmit

  • should_send(data, timeout = 500.milliseconds) -> expects the module to respond with the data provided

  • expect_send(timeout = 500.milliseconds) -> returns the next Bytes sent by the module (useful if the data sent is not deterministic, i.e. has a time stamp)

A common test case is to ensure that module state updates as expected after transmitting some data to it:

Testing HTTP requests

The test suite emulates a HTTP server so you can inspect HTTP requests and send canned responses to the module.

Use expect_http_request to access an expected request coming from the module.

  • when the block completes, the response is sent to the module

  • you can see request object details here: https://crystal-lang.org/api/latest/HTTP/Request.html

  • you can see response object details here: https://crystal-lang.org/api/latest/HTTP/Server/Response.html

Executing functions

This allows you to request actions be performed in the module via the standard public interface.

  • exec(:function_name, argument_name: argument_value) -> response a response future (async return value)

  • You should send and responds(data) before inspecting the response.get

Testing Logic

Logic modules typically expect a system to contain some drivers which the logic modules interacts with.

Then you can define the system configuration, you can also change the system configuration throughout your spec to test different configurations.

Along with the physical system configuration you can test different setting configurations. Settings can also be changed throughout the life cycle of your spec.

An action you perform on your driver might be expected to update state in the mock devices. You can access this state via the system helper

All status queried in this manner is returned as a JSON::Any object

Publishing events

Emulating notifications is also possible

name: bookings-import
version: 1.0.0

# PlaceOS API integration
dependencies:
  placeos:
    github: placeos/crystal-client
    branch: master
require "option_parser"
require "placeos"

username = "[email protected]"
password = "development"
client_id = "72a17dc552329402d68b6b02191c8f"
client_secret = "f1162a1dbe96a1dc15c33cdbe41bddb98dfd107b28e00d7aff306db60d454c8a6a507c"
place_domain = "https://placeos.place.tech"

calendar_module_id = "mod-FSqCVJUOP48"
bookings_driver_id = "driver-FTIqL3xTyeD"

# Command line options
OptionParser.parse do |parser|
  parser.banner = "Usage: #{PROGRAM_NAME} [arguments]"

  parser.on("-m MODULE_ID", "--module=MODULE_ID", "the calendar module id to be shared in all bookable spaces") do |mod|
    calendar_module_id = mod
  end

  parser.on("-b DRIVER_ID", "--booking=DRIVER_ID", "the bookings driver that we want in each room") do |driver|
    bookings_driver_id = driver
  end

  parser.on("-u USERNAME", "--user=USERNAME", "username to use for import") do |user|
    username = user
  end

  parser.on("-p PASSWORD", "--pass=PASSWORD", "password to use for import") do |pass|
    password = pass
  end

  parser.on("-d DOMAIN", "--domain=DOMAIN", "the domain of the PlaceOS server") do |dom|
    place_domain = dom
  end

  parser.on("-c CLIENT", "--client=CLIENT", "placeos application client") do |c|
    client_id = c
  end

  parser.on("-s SECRET", "--secret=SECRET", "placeos application secret") do |s|
    client_secret = s
  end

  parser.on("-h", "--help", "Show this help") do
    puts parser
    exit 0
  end
end

# Ignore certificate errors (where self-signed certificate is in use)
class OpenSSL::SSL::Context::Client
  def initialize(method : LibSSL::SSLMethod = Context.default_method)
    super(method)

    self.verify_mode = OpenSSL::SSL::VerifyMode::NONE
    <div data-gb-custom-block data-tag="if" data-0='1.0.2' data-1='1.0.2' data-2='1.0.2' data-3='1.0.2' data-4='1.0.2' data-5='1.0.2' data-6='0' data-7='0' data-8='0' data-9='0'>

      self.default_verify_param = "ssl_server"
    

</div>
  end
end

# Configure the PlaceOS client
client = PlaceOS::Client.new(place_domain,
  email: username,
  password: password,
  client_id: client_id,
  client_secret: client_secret
)

system_count = 0
created = 0
updated = 0
errors = 0
puts "grabbing level zones..."

# Grab all the zones
zones = client.zones.search(limit: 1000, tags: "level")
puts "found #{zones.size} zones"

zones.each do |zone|
  puts "checking systems in #{zone.name}..."

  # Grab the systems in each zone
  systems = client.systems.search(limit: 1000, zone_id: zone.id)
  system_count += systems.size

  # Make sure all the systems have the calendar module and a bookings driver - if email set
  systems.each do |system|
    next unless system.email.presence

    if !system.modules.includes?(calendar_module_id)
      system.modules << calendar_module_id
      begin
        client.systems.update(
          id: system.id,
          version: system.version,
          modules: system.modules,
        )
        updated += 1
      rescue error
        errors += 1
        puts error.inspect_with_backtrace
      end
    end

    # check if the any of the modules are a Bookings module
    modules = system.modules.dup
    modules.delete(calendar_module_id) # we can safely ignore this
    module_found = false
    modules.each do |mod_id|
      if client.modules.fetch(mod_id).driver_id == bookings_driver_id
        module_found = true
        break
      end
    end

    # Add the module to the system
    if !module_found
      module_id = client.modules.create(
        driver_id: bookings_driver_id,
        control_system_id: system.id,
      ).id

      begin
        client.modules.start(module_id)
      rescue
        puts "failed to start #{module_id}"
        errors += 1
      end

      created += 1
    end
  end
end

puts "\nchecked #{system_count} systems,\nupdated #{updated} systems,\ncreated #{created} modules."

if errors == 0
  puts "success"
else
  puts "#{errors} errors"
end
variable = 34
variable.should eq(34)
# transmit some data
transmit(">V:2,C:11,G:2001,B:1,S:1,F:100#")

# check that the state updated as expected
status[:area2001].should eq(1)
expect_http_request do |request, response|
  io = request.body
  if io
    data = io.gets_to_end
    request = JSON.parse(data)
    if request["message"] == "hello steve"
      response.status_code = 202
    else
      response.status_code = 401
    end
  else
    raise "expected request to include dialing details #{request.inspect}"
  end
end

# check that the state updated as expected
status[:area2001].should eq(1)
# Execute a command
response = exec(:scene?, area: 1)

# Check that the command causes the module to send some data
should_send("?AREA,1,6\r\n")
# Respond to that command
responds("~AREA,1,6,2\r\n")

# Check if the functions return value is expected
response.get.should eq(2)
# Check if the module state is correct
status[:area1].should eq(2)
# define mock versions of the drivers it will interact with

class Display < DriverSpecs::MockDriver
  include Interface::Powerable
  include Interface::Muteable

  enum Inputs
    HDMI
    HDMI2
    VGA
    VGA2
    Miracast
    DVI
    DisplayPort
    HDBaseT
    Composite
  end

  include PlaceOS::Driver::Interface::InputSelection(Inputs)

  # Configure initial state in on_load
  def on_load
    self[:power] = false
    self[:input] = Inputs::HDMI
  end

  # implement the abstract methods required by the interfaces
  def power(state : Bool)
    self[:power] = state
  end

  def switch_to(input : Inputs)
    mute(false)
    self[:input] = input
  end

  def mute(
    state : Bool = true,
    index : Int32 | String = 0,
    layer : MuteLayer = MuteLayer::AudioVideo
  )
    self[:mute] = state
    self[:mute0] = state
  end
end
DriverSpecs.mock_driver "Place::LogicExample" do

  # Where `{Display, Display}` is referencing the `MockDriver` class defined above
  # and `Display:` is the friendly name
  # so this system would have `Display_1`, `Display_2`, `Switcher_1`
  system({
    Display:  {Display, Display},
    Switcher: {Switcher},
  })

  # ...
end
DriverSpecs.mock_driver "Place::LogicExample" do

  settings({
    name: "Meeting Room 1",
    map_id: "1.03"
  })

end
DriverSpecs.mock_driver "Place::LogicExample" do

  # execute a function in your logic module
  exec(:power, true)

  # Check that the expected state has updated in you mock device
  system(:Display_1)[:power].should eq(true)

  # manually execute a function on a mock device
  # need to cast the mock to the appropriate class
  system(:Display_1).as(Display).power false
end
DriverSpecs.mock_driver "Place::LogicExample" do
  publish("channel/path", {payload: "data"}.to_json)
end
Driver Spec API
spec expectations
how to use expectations

Locating Users on a Network

Locating users on a network with PlaceOS

Using existing infrastructure, there is typically enough data available to accurately locate staff. Wireless networks provide a rough indication of location. Cabled infrastructure accurately shows who is sitting at individual desk locations.

You can also augment this with sensors as required. Sensors show desk usage and will need to fall back to Wi-Fi for staff location.

The Lookup Process:

  1. Lookup the username or email address of the person in question (staff search)

  2. Grab the device mappings for that user (as per the diagram above)

  3. Check if any of those devices are plugged in to a switch port (or have a desk reserved)

  4. If not, fall back to wireless lookup of username, email or wireless MAC address

Desk Locating Requirements

  • Switch IP addresses

  • SNMP or SSH service enabled on the switch (SSH preferred as it's easier to troubleshoot and secure)

  • A list of switch ports to desk mappings

  • A method for pairing staff to their devices

Most switches expose a SNMP service for locating details of port usage and the devices connected to each port. This is an industry standard common to most network hardware manufacturers. CISCO switches support SSH and PlaceOS supports SSHv2 for secure data transfer.

Laptop Docking Stations

Desk locating relies on device MAC addresses to identify staff as they move around a building. Docking stations often sit between the laptop and the switch. We need to ensure that the MAC address exposed by the docking station is unique to each staff member.

All commercial docking solutions offer a method for passing on a unique MAC, or do this by default. Two of the more common docking solutions are HP (BIOS or EFI configuration) and Displaylink USB docks (Dell, Lenovo, Fujitsu, Targus, Kensington, HP and Toshiba among others).

Display Link provides a PowerShell script to automate the configuration which can be deployed via SCCM.

https://support.displaylink.com/knowledgebase/articles/613455-how-to-configure-displaylink-ethernet#macclone

This alternative script provides detailed logging which can be useful when deploying.

User Device Discovery

We automate the mapping of laptops and phones to staff.

This is a two step process.

  1. Firstly we need to discover the IP addresses of the devices in use by a user

  2. Once we have the IP address we need to find the associated MAC addresses

This maintains a mapping of MAC addresses to user accounts which can be used in conjunction with port usage to determine the location of users.

User Account To IP Address Mapping

There are multiple ways to obtain this information and these can be used simultaneously.

  • Users connecting to the staff application

  • Users logging on to their machines triggering an event on the Windows domain controller

  • Users connecting to a file share or print server

  • Custom tray application locating the logged in user, any IP address changes and associated MAC addresses

Windows Domain Controller

The Windows domain controller is used to authenticate users as they log onto a device. This would typically be a laptop, desktop computer or thin client.

By auditing credential validation events it's possible to query these logs to inform PlaceOS of the user account and the corresponding IP address associated with the event.

https://technet.microsoft.com/en-us/library/dd772679%28v=ws.10%29.aspx

File Share Or Print Server

Similar to the Windows domain controller method, audit logging can be enabled for file share access events.

https://technet.microsoft.com/en-us/library/dn311489%28v=ws.11%29.aspx

https://blogs.technet.microsoft.com/mspfe/2013/08/26/auditing-file-access-on-file-servers/

IP Address To Mac Address Resolution

At this point we have a user account and an IP address. We need to lookup the MAC address associated with the IP address so we can associate the user to the MAC address / device.

  • PlaceOS will communicate with the switches over UDP port 161 or TCP port 22

  • The switches may communicate to PlaceOS over UDP port 162 (Not required for SSH connections)

Switch DHCP Snooping Table

We query DHCP snooping tables on level 2 switches as they maintain a list of DHCP allocated IP addresses and the MAC addresses of assigned devices.

DHCP snooping is a security feature and enabling it has additional advantages beyond user locating.

If DHCP snooping is undesirable, DHCP Gleaning can be used instead.

Example PowerShell Scripts

This covers the basics of user discovery using a domain controller. A third-party machine can be configured to query server logs remotely - see the detailed scripts for how this is achieved

It's possible to use additional events and modify scripts as required for security compliance.

For more details on how this is implemented please see our detailed configuration guide.

Wireless Location

There are many methods for locating users on the wireless network and PlaceOS has integrations for various systems.

  • CISCO CMX

  • CISCO Meraki

  • HP Aruba (Analytics and Location Engine

  • Ruckus SPoT

  • Huawei WLAN positioning

  • Microsoft FindMe

Depending on the wireless solution in place and it’s level of integration we may still need server notification scripts even in a wireless only environment.

  • CISCO CMX for instance can provide usernames when users are connected to the wireless, if configured to do so.

  • Microsoft FindMe will always provide a username

  • Huawei provides raw RSSI values, IP and MAC addresses. PlaceOS manages floor fingerprinting and estimating location based on these values

Shared Desktop Configuration

For monitoring desktop computer usage, such as locating a computer in a call center, you need to capture log-off events. You can only get these from the machine itself.

This is a simplified overview of data extraction from auditing events

Enable Auditing In

User Notification Script

This script can be modified to filter notifications for a subset of users, such as those in a specific domain or IP range.

Filename: user_ip.ps1

param (
    [Parameter(Mandatory=$true)][string]$ip,
    [Parameter(Mandatory=$true)][string]$username,
    [Parameter(Mandatory=$true)][string]$domain
)
$postParams = ConvertTo-Json @{module="LocateUser";method="lookup";args=@(,@($ip,$username,$domain))}
Invoke-WebRequest -Uri http://yourcompany.com/control/api/webhooks/trig-O6AXyP7jb5/notify?secret=f371579324eb56659b2f0b2c6f43d617 -Method POST -Body $postParams -ContentType "application/json"

Create An Action For The Event

  1. Create a basic task that runs an application (click through next in the wizard)

    • Program / Script: PowerShell

    • Arguments: -ExecutionPolicy Bypass -windowstyle hidden c:\aca-apps\user_ip.ps1 -ip $(ip) -username $(username) -domain $(domain)

  2. Open Task Scheduler -> Event Viewer Tasks -> (task name)

  3. Export the task

  4. Add the ValueQueries (below, more details)

  5. Delete the task

  6. Import the edited task

<Triggers>
  <EventTrigger>

    <!-- Add this section: -->
    <ValueQueries>
      <Value name="ip">Event/EventData/Data[@Name='IpAddress']</Value>
      <Value name="username">Event/EventData/Data[@Name='TargetUserName']</Value>
      <Value name="domain">Event/EventData/Data[@Name='TargetDomainName']</Value>
    </ValueQueries>
    <!-- ================= -->
    <Subscription><!-- Filter on logon types --></Subscription>

  </EventTrigger>
</Triggers>

Name

Name_format

Friendly_name

email

urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified

Email Address

first_name

urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified

Given Name

last_name

urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified

Surname

email

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress

first_name

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname

last_name

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname

login_name

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/objectidentifier

Azure AD
AD FS
Auth0
Google Workspace
SAML Decoder
Pretty Print
clicking here
contrast checker
Base Building Floor Plan
Illustrator Preferences
Make sure scale corners and scale strokes and effects are selected.
Select "Units" and double check everything is set to "Pixels"
Layers
Floor Dimensions
Outlining Floors
Space Highlights
Compare floor plans
Completing Layer
Adding Bookable Rooms
Booking ID
Adding Text
Adding Icons
Checking Names and ID
Exporting

Microsoft Outlook Plugin

PlaceOS Provide a plugin for Microsoft Outlook to facilitate room bookin

Adding Outlook Plugin to PlaceOS

The Outlook Plugin is treated like a normal Frontend Repository in PlaceOS Backoffice.

To add the plugin, you will require administrator access to PlaceOS. You can follow the instructions below or reference the Deploy a Frontend Interface document.

  1. Navigate to the Repositories tab in Backoffice.

  2. Create a new Repository.

  3. For repository name enter: Outlook Plugin

  4. For repository URL enter: https://github.com/placeos/user-interfaces

  5. For repository branch enter: build/outlook-rooms-addin/prod

  6. For folder name enter: outlookplugin

  7. Ensure repository type is set to Interface

  8. Enter GitHub credentials.

  9. You should now be able to pull the repository and reference it in domains as outlookplugin i.e. https://company.placeos.com/outlookplugin/

  10. If configured correctly, the URL should load the frontend and redirect to Microsoft Authentication.

Outlook Plugin displayed in Web Browser after following configuration steps.

Create API Endpoint in Azure

To enable the application to authenticate and return a user token to PlaceOS, we need to create an API Endpoint in Azure App Registrations.

If you have previously configured Azure App Registrations for PlaceOS Room Booking or Calendar integration, you can simply add the endpoint to the existing App Registration on Azure.

Add Azure API Endpoint

These instructions are taken directly from Microsoft Documentation: Configure Azure AD App for Office add-in.

  1. Login to Azure Portal

  2. Navigate to Azure Active Directory -> App Registrations.

  3. Select the PlaceOS User Authentication app registration.

  4. Select the Expose an API blade.

  5. Set the App ID URI to your PlaceOS Domain, Azure will automatically add your App ID to the end of the URL. Note down the App ID URI as you will need this for the manifest file.

  6. Add a new scope, configure it with the following details:

    • Scope Name: access_as_user

    • Who can consent: Admin & Users.

    • Admin consent name: Access User and Room Calendars.

    • Admin consent description: All the app to read the user calendar and calendars of rooms the user has permission to view/book.

    • User consent name: Access User and Room Calendars.

    • User consent description: All the app to read the user calendar and calendars of rooms the user has permission to view/book.

    • State: Enable

  7. Click Add Client Application

  8. Add the following clients:

    • d3590ed6-52b3-4102-aeff-aad2292ab01c (Microsoft Office)

    • ea5a67f6-b6f3-4338-b240-c655ddc3cc8e (Microsoft Office)

    • 57fb890c-0dab-4253-a5e0-7188c88b2bb4 (Office on the web)

    • 08e18876-6177-487e-b8b5-cf950c1e598c (Office on the web)

    • bc59ab01-8403-45c6-8796-ac3ef710b3e3 (Outlook on the web)

    • 93d53678-613d-4013-afc1-62e9e444a0a5 (Office on the web)

Add Redirect URI

To ensure we can obtain the user token after authentication, you will need to register a redirect URI.

  1. Login to Azure Portal

  2. Navigate to Azure Active Directory -> App Registrations.

  3. Select the PlaceOS User Authentication app registration.

  4. Select the Authentication Blade.

  5. Add a new Redirect URI and select Web.

  6. Enter your PlaceOS base domain plus /outlook/#/book/spaces for example: https://placeos.com/outlook/#/book/spaces

  7. Once the Redirect URI is added, ensure Access tokens and ID Token checkboxes are selected.

  8. Under supported account types, select only accounts in this domain.

Update Manifest File

We will need to tell Exchange your users require permissions, this must be included with the manifest.xml file which also includes the Azure App Registration API Endpoint.

  1. If it does not already exist, add a <WebApplicationInfo> element to your manifest after the closing </Resources> tag.

  2. Set the <Id> element to your App Registrations Client ID.

  3. Set the <Resource> element to the API endpoint URL provided in the API section of your App Registration.

  4. Add a <Scopes> element which includes profile, openid and User.Read

Example:

      <WebApplicationInfo>
        <Id>client-id</Id>
        <Resource>api://placeosdomain.com/client-id</Resource>
        <Scopes>
          <Scope>profile</Scope>
          <Scope>openid</Scope>
          <Scope>User.Read</Scope>
        </Scopes>
      </WebApplicationInfo>

Generate the Manifest File

To deploy the plugin to Microsoft Exchange, a Manifest File detailing the location and parameters of the application is required.

PlaceOS is able to automatically generate and serve the manifest file once you have created the API Endpoint in Azure.

To generate the manifest file, follow these steps:

  1. Navigate to PlaceOS Backoffice.

  2. Select Manage Instance from the left menu.

  3. Select Staff API.

  4. Use the drop down list to select the domain you are intending to deploy the plugin to.

  5. You should already have a Staff API Registration if you have previously integrated PlaceOS with Microsoft Services. If not you can refer to the documentation on Configuring Staff API.

  6. Edit the Staff API Registration.

  7. Enable the Configure Outlook Plugin checkbox.

  8. Enter the App ID from your Azure API Endpoint.

Register Plugin with Exchange 365

In order for the plugin to work effectively with your organisation and to allow users to add the plugin to Outlook, it must be registered in Exchange 365 Admin.

To complete these steps you will require administrator access to Exchange 365.

Register the Custom App

  1. In Exchange 365 Admin navigate to Settings -> Integrated Apps.

  2. Select Upload Custom Apps.

  3. PlaceOS provide a manifest.xml file with the user interface, in the Custom App settings select the Provide Link to Manifest file option and enter your manifest.xml URL.

  4. The Manifest URL will be per the configuration, following the example used in Adding the Outlook Plugin in this case the manifest file is located at: https://company.placeos.com/outlookplugin/manifest.xml

  5. Click Validate. The Manifest File should validate successfully.

  6. You can click Next and Finish the Wizard.

Update the Custom App

If PlaceOS make changes to the plugin, the manifest.xml file version will be incremented and these changes may be updated in the custom app.

Follow the same steps to access the Custom App under Integrated Apps in Exchange 365, then:

  1. Select the Custom Room Booking app.

  2. Select Update App under Actions.

  3. Provide your manifest.xml URL Path.

  4. Click Update.

  5. Any changes in the manifest.xml will be applied and the plugin will be updated.

Add Plugin to Outlook

If the instructions are followed correctly, shortly after registering the custom app the plugin will become visible to users in Outlook Mc & Windows.

PlaceOS will either automatically generate the Manifest file or you will be provided with a correctly formatted manifest file to upload.

  1. In Outlook, Navigate to the Tools Menu and select Get Add-ins

  2. In the add-in window, select Admin-Managed.

  3. You will see the PlaceOS Room Booking add-in.

  4. Select the Room Booking Add-in tile.

  5. Once installed, users will need to sign into the add-in with their corporate credentials.

Search for Driver
Save Calendar Driver
Start Calendar Driver
Add Bookings Driver to System
Sensor Menu
Sensor View
Extensions Domain
Select API Keys from Admin Menu
Showing the new API Key

Area Management

IoT data model for display and analytics

This driver provides the bindings for overlaying location data on a map. It exposes the following data:

  1. Overview of building use and recommended levels based on capacity

  2. A list of Areas on each level and the counts of people in those areas

  3. Location data for the levels, wireless and desks

  4. List of known desks on the level, for simplified processing of real-time visualizations

Each building needs one of these modules, and they rely on for collecting data.

Configuration

By default the view updates once a minute, you can increase this batching rate based on requirements. Intervals lower than about 15 seconds are not worth using, as it's still limited by systems which update once per minute. Locations Services can also inform Area Management of updates. This increases the batching rate to at most every 3 seconds.

You'll also need to configure the PlaceOS Staff API driver for getting zone data via the public API

Data Model

This is the state data that is exposed. It's used for display on a real-time interface and for analytics. Frontends can binding to the data points described below.

Overview

This provides an overview of the building.

Desk IDs

This provides the list of desk IDs in a system as a binding, allowing the frontend to highlight available desks.

  • Available desks = all desks - (in-use or booked)

Location Data

This binding has all the location data for a level (desks and wireless). It allows for desk highlighting and a point cloud.

The data returned doesn't include usernames. It returns MAC addresses (if available) which can resolve to a user via the LocationServices module.

Use LocationServices.check_ownership_of(mac_address) to determine who is at any of the locations

Areas

It's possible to have the system count the number of devices in an area on the map. These areas, like desk ids, are defined in metadata using a Backoffice plugin.

Desk ID Metadata

The desk ID metadata is stored in level zone metadata

  • With the key desks

  • The id of the desks are extracted from this data

Area Metadata

Area metadata is stored in level zone metadata

  • With the key map_regions

  • The area_id is extracted from this data

Defining Area Metadata

Areas are defined using a Backoffice plugin. To enable the plugin:

  1. From the domains tab, select the domain you wish to enable

  2. Select the config settings tab for that domain

  3. Enter the following configuration

Once configured, there is an extra tab added to zones where you can graphically define areas on the map

# The building this driver is monitoring
building: zone-12345

# Poll rate in seconds
poll_rate: 15

# 1.0 == no duplicate devices
# 0.8 == 20% of devices are probably duplicates (phone + laptop)
# NOTE:: that phones turn off their wifi regularly so are often not represented in the data
duplication_factor: 0.8

# Driver to query for location information
location_service: LocationServices
    # Create a service account for API access
    username:     "[email protected]"
    password:     "put this in an encrypted tab"

    # Create or grab an application for accessing the API
    client_id:    ""
    redirect_uri: ""
# overview is the binding name
"overview": {
  # The levels are represented by the zone ids
  "zone-Epaq-dE1DaH": {
    # Desk count based on `desks` metadata in the zone
    # if falls back to the zone.count field otherwise
    "desk_count": 268,
    "desk_usage": 0,
    # This is the capacity of the space, set in zone.capacity field
    "device_capacity": 100,
    # This is the raw device count
    "device_count": 70,
    # This is the adjusted count based on the duplication factor
    "estimated_people": 56,
    "percentage_use": 56,
    # The higher the recommendation number the better for recommending
    "recommendation": 102
  },

  "zone-Epa~Jlq--l0": {
    "desk_count": 0,
    "desk_usage": 0,
    "device_capacity": 0,
    "device_count": 75,
    "estimated_people": 60,
    "percentage_use": 100,
    "recommendation": -6060
  }
}
# Bind to `level_id:desk_ids`
"zone-EmWVhHG3Bhz:desk_ids": [
  "table-01.002",
  "table-01.003",
  "table-01.004",
  "table-01.005",
  "table-01.006",
  "table-01.007",
  "table-01.008"
]
# The level id is the binding
"zone-EmWVhHG3Bhz": {
  # The locations of devices on the level (wireless and desks)
  "value": [
    {
      "location": "wireless",
      "coordinates_from": "bottom-left",
      "x": 22.389704894550505,
      "y": 7.934026150363014,
      "lon": 55.27476066828535,
      "lat": 25.20106100633537,
      "s2_cell_id": "3e5f4281459c",
      "mac": "3868a4a31a50",
      "variance": 9.62534032222287,
      "last_seen": 1603168962,
      "map_width": 79.3282194366595,
      "map_height": 48.568208517912
    },
    {
      "location": "desk",
      "capacity": 1,
      "at_location": 1,
      "map_id": "desk-4-1006",
      "mac": "66e0fd1279ce",
      "level": "zone_1234",
      "building": "zone_1234"
    }
  ],

  # These are Time Series hints on how to store these data points
  "ts_hint": "complex",

  # convert x to xloc when storing in InfluxDB
  "ts_map": {
    "x": "xloc",
    "y": "yloc"
  },

  # tag this data so it's indexed (improving searching)
  "ts_tag_keys": [
    "s2_cell_id"
  ],
  "ts_tags": {
    "pos_building": "zone-FzNP2idoAOj",
    "pos_level": "zone-FzNYawvBxWD"
  }
}
# the binding is `level_id:areas`
"zone-FBkhCVwD3Af:areas": {
  # Array of area counts, the polygon coordinates are in the metadata
  "value": [
    {
       "area_id": "area-1234",
       "name": "Level 1 Desks",
       "count": 15
    },
    {
      "area_id": "zone-09.C",
      "name": "Level 1 Lounge",
      "count": 4
    }
  ],

  # Time Series storage hints
  "ts_hint": "complex",

  # add this field to the records above
  "ts_fields": {
    "pos_level": "zone-FzNYawvBxWD"
  },

  # add this tag
  "ts_tags": {
    "pos_building": "zone-FzNP2idoAOj"
  }
}
[
  {
    "bookable": false,
    "group": null,
    "id": "table-01.002",
    "name": "Desk 002"
  },
  {
    "bookable": true,
    "group": null,
    "id": "table-01.003",
    "name": "Desk 003"
  }
]
{
  # Map information
  "url": "https://s3-ap-southeast-2.amazonaws.com/os.place.tech/mck-placeos-poc.aca.im/16049826805781672737.svg",
  "width": 100,
  "height": 55.975,

  # Area definitions
  "areas": [
    {
      "id": "zone-10.C",
      "type": "Feature",
      "feature_type": "section",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
              0.738,
              0.5
          ],
          [
              0.337,
              0.5
          ],
          [
              0.337,
              0.881
          ],
          [
              0.738,
              0.881
          ]
        ]
      }
    }
  ]
}
{
  "backoffice": {
    "extend": {
      "zones": {
        "Map": {
          "conditions": [
            [
              "map_id",
              "truthy",
              ""
            ]
          ],
          "url": "https://map-region.editor.place.tech?src={{map_id}}"
        }
      }
    }
  }
}
Location Services
Sensors Config
Admin Extensions
Extension Added
Select API Keys from Admin Menu
Add new API Key Form
Remove API Key
Save Bookings Driver
Add Bookings Driver
Add Calendar Driver
Calendar Driver Config
Add Calendar Driver
Add Existing Driver
Press new Application
Navigate to Domains

Deploy AWS Fargate on Modular CloudFormation Stacks

Deployment guide for PlaceOS on Modular AWS CloudFormation templates.

Overview

This page assists with deploying PlaceOS on AWS using CloudFormation templates. From VPC configuration to a functioning application, it's all modular by design.

A CloudFormation template specifies all the components. Each component is designed to deploy as its own CloudFormation stack.

The configurable components and the templates used, in order of recommended steps, are:

  • VPC vpc.yml

  • Security Groups sec_groups.yml

  • Elasticsearch elasticsearch.yml

  • ElastiCache elasticache-redis-cluster.yml

  • Application Load Balancer load-balancer-https.yaml

  • Elastic File System EFS.yml

  • Fargate Cluster ecs-cluster.yml

  • RethinkDB rethinkdb-primary.yml

  • etcd etcd-service.yml

  • dispatch dispatch-service.yml

  • NGINX nginx-service.yml

  • Frontends frontends-service.yml

  • Auth auth-service.yml

  • Core core-service.yml

  • triggers triggers-service.yml

  • Rubber Soul rubber-soul-service.yml

  • REST API rest-api-service.yml

  • init init-service.yml

AWS EnvironmentName parameter and Stack naming

The EnvironmentName parameter's uses include:

  • Tagging

  • Service discovery

  • Linking outputs of templates with inputs of later templates

For example, the RethinkDB service uses EFS shares for it's /data directory. To pick up the correct EFS share, the templates for EFS and RethinkDB must have the same EnvironmentName.

Use the same EnvironmentName variable throughout the deployment process. PlaceOS is the default but each deployment in the same VPC should configure its own EnvironmentName. The Stack name you choose for each component should be descriptive but has no effect on the function of the deployment.

VPC Architecture: vpc.yml

The VPC template deploys two private and two public subnets. For each of these, you can configure:

  • CIDR ranges

  • An internet gateway

  • Two NAT gateways

  • Routes and route tables

The application load balancer is the only component that should deploy in public subnets.

These values have presets, but you can specify any EnvironmentName and CIDR ranges you want.

The EnvironmentName parameter chosen here should be the same for all other consequent templates

Security Groups: sec_groups.yml

Once the VPC is available, use this template to configure all the AWS Security Groups for the remaining stacks. The stacks and services are on a serverless and managed architecture. Use security groups to prevent unnecessary communications between the application components.

Use the same EnvironmentName parameter as the VPC and select the VPC created in the previous step.

Elasticsearch (AWS Managed Service): elasticsearch.yml

After configuring the security groups, you can begin configuring the components that use them. By default, this template deploys an Elasticsearch cluster version with 2 instances of t2.small.Elasticsearch.

AWS tags which identify the associated components are:

Security Group

(Elasticsearch | {EnvironmentName} | security group).

Private Subnets

(Private Subnet 1 | {EnvironmentName}) (Private Subnet 2 | {EnvironmentName})

You might see a message relating to an IAM Service Linked Role for Elasticsearch. It prevents this stack from deploying if you haven't already set up AWS Elasticsearch with your account. You can address this by:

  1. Un-commenting the section beginning with ESSLRole: in the sec_groups.yml file

  2. Redeploy the Security Groups stack using amended sec_groups.yml

  3. Redeploy this (Elasticsearch) stack

ElastiCache (AWS Managed Service): elasticache-redis-cluster.yml

By default, this template deploys an ElastiCache Redis cluster with Multi-AZ support. It comprises of one Node.js Group and two cache.t2.micro replicas as default. The Redis port, snapshot and maintenance window parameters have default values but are configurable.

Here you should use the security group with AWS tags

Security Group:

(ElastiCache | {EnvironmentName} | security group)

Subnets:

(Private Subnet 1 | {EnvironmentName}) (Private Subnet 2 | {EnvironmentName})

Application Load Balancer: load-balancer-https.yaml

This template deploys an external, public facing load balancer. It forwards public traffic to internal services.

The application load balancer is the only component that should deploy in public subnets. It requires you use your own preconfigured certificate from AWS Certificate Manager. It's configured with a certificate Identifier from ACM.

A Secure Listener serves HTTPS traffic. All inbound HTTP traffic redirects to its matching HTTPS.

Subnets:

(Public Subnet 1 | {EnvironmentName}) (Public Subnet 2 | {EnvironmentName})

VPC:

({EnvironmentName} | VPC)

Elastic File System: EFS.yml

As the application deployment consists of ephemeral containers, EFS is the persistent storage layer. RethinkDB, NGINX and Frontends services use the Elastic File System created by this template. EFS is the RethinkDB data directory. NGINX and Frontends us it for file storage of the Backoffice user interface.

Security Groups:

  • SecurityGroupEFSNginxFrontends:

(NGINX and Frontends -> EFS | {EnvironmentName} | security group)

  • SecurityGroupEFSRethinkDB:

(RethinkDB -> EFS | {EnvironmentName} | security group)

Subnets:

(Private Subnet 1 | {EnvironmentName}) (Private Subnet 2 | {EnvironmentName})

VPC:

({EnvironmentName} | VPC)

Fargate Cluster: ecs-cluster.yml

This template deploys the Elastic Container Service (ECS) cluster for all PlaceOS container services. The ECS Cluster configuration requires the same EnvironmentName parameter as prior steps.

Notes on deploying the Fargate services.

The Fargate components of the deployment still need configuration. They share a lot of common parameters. Each template configures and deploys an ECS Task Definition, Service and Task. You can configure the ECS parameters as required but the default values specified are okay for this example.

The ServiceName parameter configured will result in creation of a Service Discovery record as

{ServiceName}.{EnvironmentName}

For example, rethinkdb-primary.placeos would be the record for the RethinkDB template. All future services that need configuration with the database can use this value as an input parameter.

The security groups to use for each template will appear in the appropriate sections below. Tags for VPC and Private subnets are:

Subnets:

(Private Subnet 1 | {EnvironmentName}) (Private Subnet 2 | {EnvironmentName})

VPC:

({EnvironmentName} | VPC)

Configure the EnvironmentName parameter as in all previous steps.

RethinkDB rethinkdb-primary.yml

RethinkDB serves as the database for PlaceOS. RethinkDB can operate as a cluster but for the purposes of this example you will deploy a single primary member.

The RethinkDB /data directory uses an EFS share as created earlier by the Elastic File System stack. Data stored by the database will persist if the container task gets restarted or destroyed.

Service Discovery created:

{ServiceName}.{EnvironmentName}

E.g. rethinkdb-primary.placeos

Security Group:

(RethinkDB | {EnvironmentName} | security group)

etcd: etcd-service.yml

etcd is the service discovery layer for PlaceOS.

Service Discovery created:

{ServiceName}.{EnvironmentName}

E.g. etcd.placeos

Security Group:

(etcd | {EnvironmentName} | security group)

Dispatch: dispatch-service.yml

Dispatch allows drivers to register new servers to enable devices to connect to PlaceOS.

Service Discovery created::

{ServiceName}.{EnvironmentName}

E.g. dispatch.placeos

Security Group:

(Dispatch | {EnvironmentName} | security group)

NGINX: nginx-service.yml

NGINX is the web server for PlaceOS. See more on our implementation here

Service Discovery created:

{ServiceName}.{EnvironmentName}

E.g. nginx.placeos

Security Group:

(NGINX | {EnvironmentName} | security group)

Frontends: frontends-service.yml

Frontends is a sidecar to the web server. It listens for published frontend repositories and clones them to the NGINX static folder on EFS.

Required Services:

  • RethinkDB

Service Discovery created:

{ServiceName}.{EnvironmentName}

E.g. frontends.placeos

Security Group:

(Frontends | {EnvironmentName} | security group)

This is the first service to reference a preconfigured service parameter. RethinkDBHost references the RethinkDB service and defaults to rethinkdb-primary.placeos. You may need to change this if you have used a different EnvironmentName or ServiceName value. The same logic applies to the RethinkDBPort and RethinkDBDB parameters.

Auth: auth-service.yml

Auth provides the authentication service and API gatekeeper.

This service references the ElastiCache cluster configured earlier. Configure the Primary Endpoint from Redis here. The RedisURL parameter will have the form redis://{Primary Endpoint}:{port}

Required Services:

  • Redis

  • RethinkDB

Service Discovery created::

{ServiceName}.{EnvironmentName}

E.g. auth.placeos

Security Group:

(Auth | {EnvironmentName} | security group)

Core: core-service.yml

Core is the coordination service for running drivers on PlaceOS.

Required Services:

  • Redis

  • RethinkDB

  • etcd

Service Discovery created:

{ServiceName}.{EnvironmentName}

E.g. core.placeos

Security Group:

(Core | {EnvironmentName} | security group)

Triggers: triggers-service.yml

Triggers is the PlaceOS service for handling events and conditional triggers.

Required Services:

  • Redis

  • RethinkDB

  • etcd

Service Discovery created:

{ServiceName}.{EnvironmentName}

E.g. triggers.placeos

Security Group:

(Triggers | {EnvironmentName} | security group)

Rubber Soul: rubber-soul-service.yml

Rubber Soul is a service that connects to rethinkdb-orm models and generates Elasticsearch indices.

This service references the Elasticsearch cluster configured earlier. Configure the ESURI with the Elasticsearch VPC Endpoint which will have the form https://xxxxxx...es.amazonaws.com.

Required Services:

  • Elasticsearch

  • RethinkDB

Service Discovery created::

{ServiceName}.{EnvironmentName}

E.g. rubber-soul.placeos

Security Group:

(Rubber Soul | {EnvironmentName} | security group)

REST API: rest-api-service.yml {REST-API}

This template configures and deploys the REST API ECS Task Definition, Service and Task. REST API is an API service for interacting with PlaceOS.

Required Services:

  • Elasticsearch

  • RethinkDB

  • Redis

  • etcd

  • Frontends

  • Rubber Soul

  • Triggers

Service Discovery created::

{ServiceName}.{EnvironmentName}

E.g. rest-api.placeos

Security Group:

(Rest-api | {EnvironmentName} | security group)

init: init-service.yml

init initializes the PlaceOS instance and is the final step in the deployment. This service requires the DNS Name of the ALB as it's the URL for accessing the application. The email and password configured here will also create a login you can use once you deploy the application.

Required Services:

  • RethinkDB

  • Auth

  • Application Load Balancer {DNS Name}

Service Discovery created::

{ServiceName}.{EnvironmentName}

E.g. init.placeos

Security Group:

(init | {EnvironmentName} | security group)

This service will never actually finish as the task will exit after it has run. You can update the ECS Service to have zero Number of tasks once it has been successful.

Accessing the deployed PlaceOS Backoffice application

Once you have completed the above steps, the Backoffice application will be available at:

https://[Application_Load_Balancer_DNS_NAME]/login?continue=/backoffice

The credentials are the email and password set by the init service.

Configure Staff API

How to Configure the Staff API on PlaceOS

The Staff API gives PlaceOS the ability to interact with Calendar and User Resources.

To integrate room booking into your existing calendar environment you must configure Staff API.

Once you have configured Staff API, PlaceOS can:

  • Create

  • Edit

  • Update

  • Delete Calendar events in Microsoft 365 or Google Workspace.

To show room status on floor maps, the .

To enable room bookings, the .

Prerequisites

  • Access on Microsoft Azure or Google Cloud Console & Workspace to create apps and API permissions

  • Administrator access to your PlaceOS Backoffice

Configure Providers

To enable Staff API you must complete the necessary configuration in your cloud provider.

Instructions for Microsoft 365 and Google Workspace are below.

Microsoft Azure (365)

To use Staff API with 365 you will need to create an Application in App Registration.

You may have already completed this step if you have configured

If you have already created an app, you ca skip to Grant Graph API Permissions.

If not, you will need to create a new App Registration on Azure.

Create Azure App

  1. Navigate to the

  2. Log in and select the correct Subscription for your application

  3. Navigate to

  4. Select New Registration

  5. Enter the required information

    • Name it and select the appropriate "Support Account types" (typically "Single tenant")

    • Optionally paste the PlaceOS Assertion URL

  6. Register the app

Grant Graph API Permissions

You will now need to grant Graph API Permissions on your App.

  1. Select the app you would like to give permissions

  2. Click API Permissions

  3. Click Add Permission

  4. Click Microsoft Graph

  5. Select Delegated permissions

  6. Grant API Access to the following resources (or whichever resources are approved by the Azure administrator):

    • openid

    • offline_access

    • Calendars.ReadWrite

    • Calendars.ReadWrite.Shared

    • Group.Read.All

    • User.Read

    • User.Read.All

    • Contacts.Read

    • Place.Read.All

  7. Click Add Permissions

  8. Grant Admin Consent for all the new permissions

Generate Azure API Secret

You will now need to create the secret to allow PlaceOS Staff API to Authenticate.

  1. Navigate to Certificates & Secrets

  2. Select New client secret

  3. Give your secret a description e.g. PlaceOS Prod App Secret and click Add

  4. Copy and Save the Secret Value (you will need this in the next step)

  5. Return to the App Overview

  6. Copy and Save the Client ID and Tenant ID (you will need these in the next step)

Google Workspace

To use Google APIs you will need a server to server OAuth2 application configured.

This involves creating a service account that PlaceOS will use for authentication.

The service account can “act as” staff in the organization.

The service account can perform actions on behalf of a user, such as booking meeting rooms.

For further information see .

Configure Google Cloud API Project

  1. Go to

  2. Configure an existing API Project or Create a New API Project

  3. Open your API Project and select APIs & Services followed by Dashboard

  4. Select Enable APIs and Services

  5. Search for and enable the following SDK:

    • Admin SDK (for staff directory)

    • Google Calendar API

    • Google Drive API (for attachments)

  6. For limiting access to a subset of the organization, you may also want to enable:

    • Marketplace SDK (not Marketplace API)

    • Drive API

Configure the Service Account

  1. Under APIs & Services, navigate to Credentials

  2. Click on the Create Credentials and select Service Account Key

  3. Create a new Service Account

  4. You can ignore the next steps in the wizard, click Done to return to the list of service accounts

  5. Click the service account you created and select the Keys tab to create an access key

  6. This will save a JSON File to your computer, you will need this information to configure the service.

  7. Once the key has saved, return to the Details tab and enable Domain Wide Delegation

  8. Click Save

Configure Service Account Permissions

If you want to configure this application for use in a subset of the organization, ignore this step.

Continue with the steps to “Create a marketplace application”

  1. Open the JSON File that we saved in the previous step

  2. Copy the client_id

  3. Navigate to

  4. Select Security

  5. Scroll down to API Controls

  6. Select Manage Domain Wide Delegation

  7. Click Add new and enter the client_id you extracted earlier

  8. Add the following API Scopes:

    • https://www.googleapis.com/auth/calendar

    • https://www.googleapis.com/auth/admin.directory.user.readonly

    • https://www.googleapis.com/auth/drive.file

  9. Click Authorize

  10. Ensure you enable API Access by going to Security -> API Controls

  11. Select Trust internal, domain owned apps

The scope https://www.googleapis.com/auth/drive.file allows the application to add attachments to calendar events, such as QR codes.

It does not allow for reading or modifying any files not created by the application.

Creating a Marketplace Application

This step applies to organizations where a specific region or department (OU) will be using the application.

This step is not applicable to most organizations.

  1. On navigate to the API Services Dashboard

  2. Select the G Suite or Google Workspace Marketplace SDK

  3. Select the Configuration tab

  4. Fill in the app name and description, un-check Enable individual install

  5. Upload icons as required

  6. A Terms of Service URL is also required, you can set this to your companies homepage

  7. Enter the following scope URL:

    • https://www.googleapis.com/auth/calendar

    • https://www.googleapis.com/auth/admin.directory.user.readonly

    • https://www.googleapis.com/auth/drive.file

  8. Enable drive extension and click Configure drive SDK

  9. Fill in the details and icons for the Drive Application

  10. Once completed, return to the marketplace application form

  11. Ensure you set visibility to My Domain

  12. Click Save Changes

Deploy the marketplace application to the organizational unit that will be using the application.

Follow the steps to .

Configure Staff API on PlaceOS

You will now need to enter the information obtained from the App Registration and API Permissions.

To complete this step, you will need the following information:

  • Microsoft Azure (all information obtained from Azure App Register)

    • Client ID

    • Tenant ID

    • Secret

  • Google Workspace

    • Domain - this is the domain your users use when logging into Google

    • Service Account Email - service account user created in previous steps

    • Scopes - what Google services are we accessing (calendar, admin groups, etc)

    • Private Key - available via the JSON downloaded from Google Cloud Console

    • Service User - a Google Workspace user with required permissions to read resource calendars/user information

    • User Agent - user defined and helps with looking at Google logs to see application actions

  1. Open PlaceOS Backoffice and login as an administrator

  2. Navigate to the Admin Tab

  3. Select Staff API

  4. Select the Domain you want to configure

  5. Click Add Tenant

  6. Enter the information required

  7. If you are configuring access for Microsoft 365 ensure you tick 'Delegated Access' and enter teamsForBusiness in the Conference Type field.

  8. Save

Test Staff API Configuration

The easiest way to test the Staff API Configuration is using the or .

The staff-api logs will show any errors in configuration.

We can view these logs by connecting to the server and running docker logs --tail 99 -f staff-api.

An example log with an authentication error to Microsoft 365:

Discovering User Devices

Map Usernames to MAC Addresses via IP Addresses

This is for mapping usernames to MAC addresses via IP address. It requires multiple points of integration and captures data from Windows Domains.

  • We use CIDR notation for IP address filtering

Remote Event Query

This grabs user device details that are interacting with the domain controller. It takes five minutes worth of details, so should run every five minutes.

  • Event result code details

  • Event details

# Required for reliable Resolve-DnsName.
import-module dnsclient

# Helper for filtering IP addresses belonging to a subnet
function checkSubnet ([string]$cidr, [string]$ip) {
    $network, [uint32]$subnetlen = $cidr.Split('/')
    $a = [uint32[]]$network.split('.')
    [uint32] $unetwork = ($a[0] -shl 24) + ($a[1] -shl 16) + ($a[2] -shl 8) + $a[3]
    $mask = (-bnot [uint32]0) -shl (32 - $subnetlen)
    $a = [uint32[]]$ip.split('.')
    [uint32] $uip = ($a[0] -shl 24) + ($a[1] -shl 16) + ($a[2] -shl 8) + $a[3]
    $unetwork -eq ($mask -band $uip)
}

# Use a password file: https://blogs.technet.microsoft.com/robcost/2008/05/01/powershell-tip-storing-and-using-password-credentials/
$User = "YourDomain\service_account"
$PWord = ConvertTo-SecureString -String "service_account_pass" -AsPlainText -Force
$Credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $User, $PWord

$results = New-Object System.Collections.Generic.List[System.Object]
$ips = @()
$events = $null

try {
    Write-Host "Requesting events from remote server...";

    $events = Get-WinEvent -ComputerName "domain.controller.com" -Credential $Credential -LogName "Security" -FilterXPath @"
    *[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and
      EventID=4768 and TimeCreated[timediff(@SystemTime) <= 320000]]] and
    *[EventData[Data[@Name='Status'] and (Data='0x0')]] and
    *[EventData[Data[@Name='TargetDomainName'] and (Data='YourDomain')]]
"@
} catch {
    Write-Host "Server found no results...";
    Write-Host $_.Exception.Message;
    exit 0
}

Write-Host "Events received from remote server";

# This makes the events look like they were requested locally
# (remote event requests come back as generic objects)
ForEach ($event in $events) {
    $eventXML = [xml]$event.ToXml()

    # Iterate through each one of the XML message properties
    For ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++) {
        # Append these as object properties
        Add-Member -InputObject $event -MemberType NoteProperty -Force `
            -Name  $eventXML.Event.EventData.Data[$i].name `
            -Value $eventXML.Event.EventData.Data[$i].'#text'
    }
}

Write-Host "IP addresses discovered:";

$events | ForEach-Object {
    try {
        $ip = $_.IpAddress
        $username = $_.TargetUserName
        $userlower = $username.ToLower()
        $domain = $_.TargetDomainName

        # Ensure the event includes the IP address
        if ([string]::IsNullOrWhiteSpace($ip) -Or ($ip -eq "-") -Or [string]::IsNullOrWhiteSpace($username) -Or [string]::IsNullOrWhiteSpace($domain)) {
            return
        }

        # Ensure IP address is of the correct type
        [IPAddress]$address = $ip
        if (($PSVersionTable.PSVersion.Major -ge 5) -Or (($PSVersionTable.PSVersion.Major -eq 4) -And ($PSVersionTable.PSVersion.Minor -ge 5))) {
            if ($address.IsIPv4MappedToIPv6) {
                $ip = $address.MapToIPv4().IPAddressToString
            } else {
                # Ignore IPv6
                if ($address.AddressFamily.ToString() -eq "InterNetworkV6") {
                    Write-Host "Ignoring IPv6 address: ", $ip
                    return
                }
            }
        } else {
            # Check IPv6 Mapping manually
            if (($address.AddressFamily.ToString() -eq "InterNetworkV6") -And $ip.StartsWith("::ffff:")) {
                $new_ip = $ip.Split("::ffff:")[-1]

                try {
                    [IPAddress]$address = $new_ip
                    if ($address.AddressFamily.ToString() -eq "InterNetwork") {
                        $ip = $new_ip
                    }
                } catch {
                    # we are not interested in this IP address
                    Write-Host "Ignoring IPv6 address: ", $ip
                    return
                }
            }
        }

        # Check the IP address hasn't been seen already
        if ($ips.Contains($ip)) { return }

        # Filter IP ranges, service accounts and computer names$
        if ( `
            ( `
                (checkSubnet "127.0.0.0/16" $ip) -Or `
                (checkSubnet "192.168.0.0/16" $ip) -Or `
                (checkSubnet "192.155.0.0/16" $ip) `
            ) -and `
            (!$userlower.StartsWith("sccm.")) -and `
            (!$userlower.StartsWith("svc.")) -and `
            ($username[-1] -ne "$") `
        ) {
            $ips += $ip
            Write-Host $ip;

            # Try to grab the computers hostname
            try {
                $hostname = (Resolve-DnsName $ip -ErrorAction SilentlyContinue)[0].NameHost
                $results.Add(@($ip,$username,$domain,$hostname))
            } catch {
                $results.Add(@($ip,$username,$domain))
            }
        }
    } catch {
        Write-Host "Error parsing event";
        Write-Host $_.Exception.Message;
    }
}

$resultArr = $results.ToArray()

# Only post to the server if there are results
if ($resultArr.length -gt 0) {
    Write-Host "Posting to control server";

    # Send to the server
    $postParams = ConvertTo-Json @($resultArr)
    $res = Invoke-WebRequest -UseBasicParsing -Uri https://placeos.server.com/api/engine/v2/webhook/trig-98UC/notify?secret=046856ff816d49f26261d3c9c9789da&exec=true&mod=LocationServices&method=ip_mappings -Method POST -Body $postParams -ContentType "application/json" -TimeoutSec 40
    Write-Host "Response code was:" $res.StatusCode;

    if ($res.StatusCode -ne 200) {
        Write-Host "Webhook post failed...";
        exit 1
    }
} else {
    Write-Host "No results found...";
}

Querying a MS Network Policy Server (RADIUS)

This allows us to grab MAC addresses of BYOD devices. Useful if tracking mobile phones on the Wi-Fi is desirable.

# Required for reliable Resolve-DnsName.
import-module dnsclient
import-module dhcpserver

# Use a password file: https://blogs.technet.microsoft.com/robcost/2008/05/01/powershell-tip-storing-and-using-password-credentials/
$User = "YourDomain\service_account"
$PWord = ConvertTo-SecureString -String "service_account_pass" -AsPlainText -Force
$Credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $User, $PWord

$results = New-Object System.Collections.Generic.List[System.Object]
$macs = @()
$events = $null

try {
    Write-Host "Requesting events from Network Policy Server...";

    $events = Get-WinEvent -ComputerName "radius.server.com" -Credential $Credential -LogName "Security" -FilterXPath @"
    *[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and
      EventID=6278 and TimeCreated[timediff(@SystemTime) <= 90000]]] and
    *[EventData[Data[@Name='SubjectDomainName'] and (Data='YourDomain')]]
"@
} catch {
    Write-Host "Server found no results...";
    Write-Host $_.Exception.Message;
    exit 0
}

Write-Host "Events received from remote server";

# This makes the events look like they were requested locally
# (remote event requests come back as generic objects)
ForEach ($event in $events) {
    $eventXML = [xml]$event.ToXml()

    # Iterate through each one of the XML message properties
    For ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++) {
        # Append these as object properties
        Add-Member -InputObject $event -MemberType NoteProperty -Force `
            -Name  $eventXML.Event.EventData.Data[$i].name `
            -Value $eventXML.Event.EventData.Data[$i].'#text'
    }
}

Write-Host "MAC addresses discovered:";

$events | ForEach-Object {
    try {
        $mac_address = $_.CallingStationID
        # Username in domain\username format
        $username = $_.FullyQualifiedSubjectUserName
        $ip = $null

        # Grab the IP address assigned to the MAC address
        try {
            $ip = Get-DhcpServerv4Scope -ComputerName "dhcpserver.contoso.com" -ScopeId 192.168.4.0 | Get-DhcpServerv4Lease -ComputerName "dhcpserver.contoso.com" | where {$_.Clientid -like "$mac_address"}
            $ip = $ip.IPAddress.IPAddressToString
        } catch {
            # Ignore errors as it just means we won't able find the hostname
        }

        # Ensure the event includes the username and device mac address
        if ([string]::IsNullOrWhiteSpace($mac_address) -Or ($mac_address -eq "-") -Or [string]::IsNullOrWhiteSpace($username) -Or ($username -eq "-")) {
            return
        }

        # Check the IP address hasn't been seen already
        if ($macs.Contains($mac_address)) { return }

        # Filter IP ranges and computer name$
        $macs += $mac_address
        Write-Host $mac_address

        # Try to grab the computers hostname
        try {
            $hostname = (Resolve-DnsName $ip -ErrorAction SilentlyContinue)[0].NameHost
            $results.Add(@($mac_address,$username,$hostname))
        } catch {
            $results.Add(@($mac_address,$username))
        }
    } catch {
        Write-Host "Error parsing event";
        Write-Host $_.Exception.Message;
    }
}

$resultArr = $results.ToArray()

# Only post to the server if there are results
if ($resultArr.length -gt 0) {
    Write-Host "Posting to control server";

    # Send to the server
    $postParams = ConvertTo-Json @($resultArr)
    $res = Invoke-WebRequest -UseBasicParsing -Uri https://placeos.server.com/api/engine/v2/webhook/trig-98UC/notify?secret=046856ff816d49f26261d3c9c9789da&exec=true&mod=LocationServices&method=mac_mappings -Method POST -Body $postParams -ContentType "application/json" -TimeoutSec 40
    Write-Host "Response code was:" $res.StatusCode;

    if ($res.StatusCode -ne 202) {
        Write-Host "Webhook post failed...";
        exit 1
    }
} else {
    Write-Host "No results found...";
}

Workstation Monitoring

This is for when users log onto a shared resource and we want to know who is sitting at which workstation. We should attach an event to particular events using the filter below. More details on how to set this up are here

  • Event Type Details

  <Triggers>
    <EventTrigger>
      <ValueQueries>
          <Value name="username">Event/EventData/Data[@Name='TargetUserName']</Value>
          <Value name="domain">Event/EventData/Data[@Name='TargetDomainName']</Value>
      </ValueQueries>
      <Enabled>true</Enabled>
      <Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Security"&gt;&lt;Select Path="Security"&gt;*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and EventID=4624]] and *[EventData[Data[@Name='LogonType'] and (Data='2' or Data='7')]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
    </EventTrigger>
  </Triggers>

This catches log off events and marks the workstation as free.

param (
    [Parameter(Mandatory=$false)][string]$username,
    [Parameter(Mandatory=$false)][string]$domain
)

# Get the IP address of the local PC
$ipV4 = Test-Connection -ComputerName $env:COMPUTERNAME -Count 1  | Select -ExpandProperty IPV4Address
$ip = $ipV4.IPAddressToString

# Ensure the event includes the IP address
if ([string]::IsNullOrWhiteSpace($ip) -Or ($ip -eq "-") -Or [string]::IsNullOrWhiteSpace($username) -Or [string]::IsNullOrWhiteSpace($domain)) {
    Write-Host "IP address was blank";
    exit 0
}

# Post to details to server
$postParams = ConvertTo-Json @(,@($ip,$username,$domain))
Invoke-WebRequest -UseBasicParsing -Uri https://placeos.server.com/api/engine/v2/webhook/trig-98UC/notify?secret=046856ff816d49f26261d3c9c9789da&exec=true&mod=LocationServices&method=ip_mappings -Method POST -Body $postParams -ContentType "application/json"

Untrusted or Self Signed Certificates

Add this to ignore certificate errors

add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

Protocol violation errors

Add this to ignore errors, see this thread on Powershell wget protocol violation

function Set-UseUnsafeHeaderParsing
{
    param(
        [Parameter(Mandatory,ParameterSetName='Enable')]
        [switch]$Enable,

        [Parameter(Mandatory,ParameterSetName='Disable')]
        [switch]$Disable
    )

    $ShouldEnable = $PSCmdlet.ParameterSetName -eq 'Enable'

    $netAssembly = [Reflection.Assembly]::GetAssembly([System.Net.Configuration.SettingsSection])

    if($netAssembly)
    {
        $bindingFlags = [Reflection.BindingFlags] 'Static,GetProperty,NonPublic'
        $settingsType = $netAssembly.GetType('System.Net.Configuration.SettingsSectionInternal')

        $instance = $settingsType.InvokeMember('Section', $bindingFlags, $null, $null, @())

        if($instance)
        {
            $bindingFlags = 'NonPublic','Instance'
            $useUnsafeHeaderParsingField = $settingsType.GetField('useUnsafeHeaderParsing', $bindingFlags)

            if($useUnsafeHeaderParsingField)
            {
              $useUnsafeHeaderParsingField.SetValue($instance, $ShouldEnable)
            }
        }
    }
}

# Call this before Invoke-WebRequest
Set-UseUnsafeHeaderParsing -Enable
level=[E] time=2021-06-24T04:10:58Z program=StaffAPI source=action-controller client_ip=218.214.254.247 request_id=50173a7d-a819-4413-8f8a-94adf592f309 domain=poc.placeos.com tenant_id=71 event=error method=GET path=/api/staff/v1/calendars/free_busy?period_start=1624507858&period_end=1624543199&zone_ids=zone-HDvnRd_9lAS status=500 duration=345.62ms
error fetching token UNAUTHORIZED (401)
{
    "error": "invalid_client",
    "error_description": "AADSTS7000215: Invalid client secret is provided.\r\nTrace ID: 6f090c0f-079f-4930-a123-b1242a4f3f00\r\nCorrelation ID: 0424faa6-2325-4c79-b0e3-726d68db7655\r\nTimestamp: 2021-06-24 04:10:58Z",
    "error_codes": [
        7000215
    ],
    "timestamp": "2021-06-24 04:10:58Z",
    "trace_id": "6f090c0f-079f-4930-a123-b1242a4f3f00",
    "correlation_id": "0424faa6-2325-4c79-b0e3-726d68db7655",
    "error_uri": "https://login.microsoftonline.com/error?code=7000215"
}
PlaceOS Calendar driver must also be configured
PlaceOS Bookings driver must be also configured
PlaceOS for Microsoft 365 User Authentication.
Azure Portal
App Registrations
Creating and Managing Service Accounts
Google Cloud Console
Google Workspace Admin
Google Cloud Console
Install a Google Workspace Marketplace App in your Domain
PlaceOS Calendar Driver
Microsoft API Calendar Driver for 365 Delegation

Writing A Driver

Everything you need to know about writing drivers for PlaceOS

There are three main uses of drivers:

  • Streaming IO (TCP, SSH, UDP, Multicast etc.)

  • HTTP Client

  • Logic

From a driver structure standpoint there is no difference between these types.

  • The same driver works over a TCP, UDP or SSH transport

  • All drivers support HTTP methods (except logic modules)

    • for example a websocket driver or tcp driver will also be provided a default HTTP client at the base URI of the websocket and IP address of the tcp driver.

    • this default client URL can be overwritten, for example where the HTTP port is different to the websocket port transport.http_uri_override = URI.new

  • All drivers have access to logic helpers when associated with a System

Code documentation

For detailed automatically generated documentation please see the: Driver API

  1. All drivers should require placeos-driver before anything else.

  2. There should be a single class that inherits `PlaceOS::Driver`

require "placeos-driver"
require "..."

class MyDriver < PlaceOS::Driver
  ...
end

Queue

The queue is a list of potentially asynchronous tasks that should be performed in a sequence.

  • Each task has a priority (defaults to 50) - higher priority tasks run first

  • Tasks have names - if there's a name conflict, the newer task overwrites the older one

  • Tasks have a timeout (defaults to 5.seconds)

  • Tasks a set amount of re-tries (defaults to 3 before failing)

Tasks have a callback which can run the task

# => you can set queue defaults globally

# set a delay between the current task completing and the next task
queue.delay = 1.second
queue.retries = 5

queue(priority: 20, timeout: 1.second) do |task|
  # perform action here

  # signal result
  task.success("optional success value")
  task.abort("optional failure message")
  task.retry

  # Give me more time to complete the task
  task.reset_timers
end

In most cases you won't need to use the queue explicitly, but it's good to understand that how it functions.

Transport

The transport loaded is defined by settings in the database.

Streaming IO

You should always tokenize your streams. You can do this automatically with the built-in tokenizer

def on_load
  transport.tokenizer = Tokenizer.new("\r\n")
end

Here are the ways to use streaming IO methods:

  1. Send and receive

def perform_action
  # You call send with some data.
  # you can also optionally pass some queue options to the function
  send("message data", priority: 30, name: "generic-message")
end

# A common received function for handling responses
def received(data, task)
  # data is always `Bytes`
  # task is always `PlaceOS::Driver::Task?` (i.e. could be nil if no active task)

  # convert data into the appropriate format
  data = String.new(data)

  # decide if the request was a success or not
  # you can pass any value that is JSON serialisable to success
  # (if it can't be serialised then nil is sent)
  task.try &.success(data)
end
  1. Send and callback

def perform_action
  request = "build request"

  send(request, priority: 30, name: "generic-message") do |data, task|
    data = String.new(data)

    # process response here (might need to know the request context)

    task.try &.success(data)
  end
end
  1. Send immediately (no queuing)

def perform_action_now!
  transport.send("no queue")
end

You can also add a pre-processor to data coming in. This can be useful if you want to strip away a protocol layer. For example, if you are using Telnet and want to remove the telnet signals leaving the raw data for tokenizing

def on_load
  transport.pre_processor do |bytes|
    # you must return some byte data or nil if no processing is required
    # tokenisation occurs on the data returned here
    bytes[1..-2]
  end
end
def received(data, task)
  # data coming in here is both pre_processed and tokenised
end

HTTP Client

All drivers have built-in methods for performing HTTP requests.

  • For streaming IO devices this defaults to http://device.ip.address (https if the transport is using TLS / SSH)

  • All devices can provide a custom HTTP base URI

There are methods for all the typical HTTP verbs: get, post, put, patch, delete

def perform_action
  basic_auth = "Basic #{Base64.strict_encode("#{@username}:#{@password}")}"

  response = post("/v1/message/path", body: {
    messages: numbers,
  }.to_json, headers: {
    "Authorization" => basic_auth,
    "Content-Type"  => "application/json",
    "Accept"        => "application/json",
  }, params: {
    "key" => "value"
  })

  raise "request failed with #{response.status_code}" unless (200...300).include?(data.status_code)
end

Special SSH methods

SSH connections will attempt to open a shell to the remote device. Sometimes you may be able to execute operations independently.

def perform_action
  # if the application launched supports input you can use the bidirectional IO
  # to communicate with the app
  io = exec("command")
end

Logic drivers

Logic drivers belong to a System and cannot be shared, which makes them different from other transports. All other drivers can appear in any number of systems.

You can access remote modules in the system via the system helper

# Get a system proxy
sys = system
sys.name #=> "Name of system"
sys.email #=> "[email protected]"
sys.capacity #=> 12
sys.bookable #=> true
sys.id #=> "sys-tem~id"
sys.modules #=> ["Array", "Of", "Unique", "Module", "Names", "In", "System"]
sys.count("Module") #=> 3
sys.implementing(PlaceOS::Driver::Interface::Powerable) #=> ["Camera", "Display"]

# Look at status on a remote module
system[:Display][:power] #=> true (JSON::Any)
system[:Display].status(Bool, :power) #=> true (Bool)
system[:Display].status?(Bool, :power) #=> true (Bool | Nil)

# Access a different module index
system[:Display_2][:power]
system.get(:Display, 2)[:power]

# Access all modules of a type
system.all(:Display)

# Check if a module exists
system.exists?(:Display) #=> true
system.exists?(:Display_2) #=> false

You can bind to state in remote modules

bind Display_1, :power, :power_changed

private def power_changed(subscription, new_value)
  logger.debug new_value
end

# you can also bind to internal state (available in all drivers)
bind :power, :power_changed

It's also possible to create shortcuts to other modules. This is powerful as these shortcuts are exposed as metadata. It allows Backoffice to perform system verification.

For example, consider the following video conference system:

# It requires at least one camera that can move and be turned on and off
accessor camera : Array(Camera), implementing: [Powerable, Moveable]

# Optional room blinds that can be opened and closed
accessor blinds : Array(Blind)?, implementing: [Switchable]

# A single display is required with an optional screen (maybe it's a projector)
accessor main_display : Display_1, implementing: Powerable
accessor screen : Screen?

Cross system communication is possible if you know the ID of the remote system.

# once you have reference to the remote system you can perform any
# actions that you might perform on the local system
sys = system("sys-12345")

sys.name #=> "Name of remote system"
sys[:Display_2][:power] #=> true

Subscriptions

You can dynamically bind to state of interest in remote modules

# subscription is returned and provided with every status update in the callback
subscription = system.subscribe(:Display_1, :power) do |subscription, new_value|
  # values are always raw JSON strings
  JSON.parse(new_value)
end

# Local subscriptions
subscription = subscribe(:state) do |subscription, new_value|
  # values are always raw JSON strings
  JSON.parse(new_value)
end

# Clearing all subscriptions
subscriptions.clear

Like subscriptions, channels can be setup for broadcasting any data that might not need be exposed as state.

subscription = monitor(:channel_name) do |subscription, new_value|
  # values are always raw JSON strings
  JSON.parse(new_value)
end

# Publish something on the channel to all listeners
publish(:channel_name, "some event")

Scheduler

There is a built-in scheduler

def connected
  schedule.every(40.seconds) { poll_device }
  schedule.in(200.milliseconds) { send_hello }
end

def disconnected
  schedule.clear
end

Settings

Settings are stored as JSON and then extracted as required, serializing to the specified type. There are two types:

  • Required settings - raise an error if the setting is unavailable

  • Optional settings - return nil if the setting is unavailable

All settings will raise an error if they exist but fail to serialize (due to incorrect formatting etc.)

# Required settings
def on_update
  @display_id = setting(Int32, :display_id)

  # Can extract deeply nested values
  # i.e. {input: {list: ["HDMI", "VGA"] }}
  @primary_input = setting(InputEnum, :input, :list, 0)
end

# Optional settings (you can optionally provide a default)
def on_update
  @display_id = setting?(Int32, :display_id) || 1
  @primary_input = setting?(InputEnum, :input, :list, 0) || InputEnum::HDMI
end

You can update the local settings of a module, persisting them to the database. Settings must be JSON serializable

define_setting(:my_setting_name, "some JSON serialisable data")

Logger

There is a logger available

  • warn and above are written to disk

  • debug and info are only available when there is an open debugging session

logger.warn { "error unknown response" }
logger.debug { "function called with #{value}" }

The logging format has been pre-configured so all logging from PlaceOS is uniform and parsed as-is

Metadata

Many components use metadata to simplify configuration.

  • generic_name => the name that a system should use to access the module

  • descriptive_name => the manufacturers name for the device

  • description => notes or any other descriptive information you wish to add

  • tcp_port => TCP port the TCP transport should connect to

  • udp_port => UDP port the UDP transport should connect to

  • uri_base => The HTTP base for any HTTP requests

  • default_settings => Default or example settings that for configuring a module

class MyDevice < PlaceOS::Driver
  generic_name :Driver
  descriptive_name "Driver model Test"
  description "This is the driver used for testing"
  tcp_port 22
  default_settings({
    name:     "Room 123",
    username: "steve",
    password: "$encrypt",
    complex:  {
      crazy_deep: 1223,
    },
  })

  # ...

end

Security

By default all public functions are exposed for execution. You can limit who is able to execute sensitive functions.

@[Security(Level::Administrator)]
def perform_task(name : String | Int32)
  queue &.success("hello #{name}")
end

Use the Security annotation to define the access level of the function. The options are:

  • Administrator Level::Administrator

  • Support Level::Support

When a user initiates a function call, within a driver, you can access that users id via the invoked_by_user_id function, which returns a String if a user initiated the call.

Interfaces

Drivers can expose any methods that make sense for the device, service or logic they encapsulate. Across these there are often core sets of similar functionality. Interfaces provide a standard way of implementing and interacting with this.

Though optional, they're recommended as they make drivers more modular and less complex.

A full list of interfaces is available in the driver framework. This will expand over time to cover common, repeated patterns as they emerge.

Implementing an Interface

Each interface is a module containing abstract methods, types and functionality built from these.

First include the module within the driver body.

include Interface::Powerable

You will then need to provide implementations of the abstract methods. The compiler will guide you in this.

Some interfaces will also provide default implementation for other methods. These may be overridden if the device or service provides a more efficient way to do the same thing. To keep compatibility, overridden methods must maintain feature and functional parity with the original.

Using an Interface

You can use the system.implementing method from any logic module. It returns a list of all drivers in the system which implement the Interface.

The accessor macro provides a way to declare a dependency on a sibling driver for a specific function.

For more information on these and for usage examples, see logic drivers.

Handling errors

Where multiple functions are likely to raise similar errors, the errors can be handled generically using the rescue_from helper.

class MyDevice < PlaceOS::Driver
  rescue_from JSON::ParseException do |error|
    logger.warn(exception: error) { "error parsing JSON payload" }
    {} of String => JSON::Any
  end

  # any external call to this function will result in the empty hash above
  # being returned to the caller. Internally in the driver the error will
  # be raised as normal.
  def no_error_externally
    JSON.parse %({invalid: 'json')
  end
end

Alternatively this can be handled via an explicit function. Useful if it's desirable to use the same code in the received function.

class MyDevice < PlaceOS::Driver
  rescue_from JSON::ParseException, :handle_parse_error

  protected def handle_parse_error(error)
    logger.warn(exception: error) { "error parsing JSON payload" }
    {} of String => JSON::Any
  end
  
  # The above might be used as follows:
  
  def no_error_externally
    # externally returns {}
    JSON.parse %({invalid: 'json')
  end
  
  # Keep error parsing DRY
  def received(data, task)
    result = JSON.parse(String.new data)
    task.try &.success(result)
  rescue error : JSON::ParseException
    result = handle_parse_error(error)
    task.try &.success(result)
  end
end
Google API Service
New App Registration
Google Domain Wide Delegation
Graph Application Grants
Add Permission
Google API Project
Google New API Project
Client ID and Tenant
Google JSON Key
API Permissions
Google Visibility
Google Scopes
Google Drive Extension
Google Service API Key
Google G Suite Marketplace
Google New Service Account
Google API Credentials
Google Client ID
New Secret
Google G Suite Config Tab
Graph
New Secret
Graph Application Grants
Staff API Admin
Google G Suite App
Google Trust Internal Apps