# Musson Industrial's Embr-Periscope Module

**URL:** <https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047>\
**Category:** 3rd Party Modules\
**Tags:** perspective\
**Created:** [November 24, 2024, 10:26pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047 "2024-11-24T22:26:31Z")\
**Posts on this page:** 20\
**Page:** 4

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [June 19, 2025, 2:49pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/62 "2025-06-19T14:49:56Z")

</div>

No, unfortunately there isn’t a way to do lazy loading for views.

I think the `virtualized` feature should work though, but I can’t say I’ve tested it; any feedback you could provide about it would be helpful.

---

<div class="post-metadata">

**Author:** ![martin.johansson](https://avatars.discourse-cdn.com/v4/letter/m/7feea3/32.png) [@martin.johansson](https://forum.inductiveautomation.com/u/martin.johansson)\
**Post date:** [June 24, 2025, 6:29am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/64 "2025-06-24T06:29:43Z")

</div>

Think I came up with a solution. Atleast it seems to work quite good.

I load images from a database stored as varbinary and I want to stop this if the Swiper contains alot of images.

- Added "index" to params on the Embedded Views (Ignition function).
- Added "load"-bool to params on the Embedded Views with a change script.

```python
def valueChanged(self, previousValue, currentValue, origin, missedEvents):
	if currentValue.value == True and self.view.custom.image == "":
		import base64
		sqlParams = {"imageId": self.view.params.imageId}
		image = system.db.runNamedQuery("getImage", sqlParams).getValueAt(0,0)
		self.view.custom.image = base64.b64encode(image)

```

- Set "load" to true for the first 3 views and the rest to false when creating the instances.
- Added a Custom Method to the view with Swiper.

```python
def realIndex(self):
	component = self.getChild("Swiper")
	
	proxy = component.getJavaScriptProxy("swiper")
	currentIndex = proxy.runBlocking('''() => this.realIndex''')
	
	if currentIndex != self.view.custom.currentIndex:
		self.view.custom.currentIndex = currentIndex
		
		for i in range(3):
			self.getChild("Swiper").props.instances[currentIndex + i].viewParams.load = True

```

- Calling the Custom Method through a "onPointerDown" event.

```python
def runAction(self, event):
	self.parent.realIndex()

```

This seems to work fine for me. But I'm not really sure how effecient it is. And depending on the Swiper settings it could cause the message to not get triggered enough (more dots (have to preload more then +2), or with a scroll etc.). If there was a better way to get the current index it would probably work better.

EDIT: Used Message Handler on the Embedded Views to load the images but felt wrong to send out a message that ~100 views handled on each Swiper-click. This solution feels better, but I'm open for a better one 🙂

---

<div class="post-metadata">

**Author:** ![liam1](https://avatars.discourse-cdn.com/v4/letter/l/e68b1a/32.png) [@liam1](https://forum.inductiveautomation.com/u/liam1)\
**Post date:** [July 30, 2025, 8:04am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/65 "2025-07-30T08:04:54Z")

</div>

Hi @bmusson,

I'm trying the chart and periscope module on ignition 8.3 beta1:  
but is seems it's not compiled for 8.3.0-beta1 yet:

```python
Error
Module "Embr Charts" requires Ignition 8.1.33 (b0) and is not compatible with Ignition 8.3.0-beta1 (b2025072515)

```

Do intend to release a version of the module for the 8.3 beta?

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [July 31, 2025, 4:54am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/66 "2025-07-31T04:54:53Z")

</div>

> [@liam1](#):
>
> Do intend to release a version of the module for the 8.3 beta?

Yup. I’m out on vacation for a while, but getting Embr compiling for 8.3 is a high priority once I get back.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [August 4, 2025, 4:03pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/67 "2025-08-04T16:03:20Z")

</div>

Embr-Periscope for the Ignition 8.3 beta can be found here:

> [@Musson Industrial's Embr v8.3 Beta Modules](https://forum.inductiveautomation.com/t/musson-industrials-embr-v8-3-beta-modules/109332):
>
> Embr v8.3 Beta Modules This thread is for updates and feedback related to Embr and the Ignition 8.3 Betas. Downloads These local build downloads will be kept up to date until I add support for automatically publishing Embr's 8.3 branch. [Embr-Charts-Ignition83Beta-3.0.1.modl](https://forum.inductiveautomation.com/uploads/short-url/dW5l6hfJylLxo0owesqcP16h66a.modl) (2.7 MB) [Embr-Periscope-Ignition83Beta-0.8.1.modl](https://forum.inductiveautomation.com/uploads/short-url/6DXQk88kLvivMJv9hOW7pX6cPjX.modl) (1.9 MB) [Embr-Thermodynamics-Ignition83Beta-1.0.4.modl](https://forum.inductiveautomation.com/uploads/short-url/6jubZryT2EEvhJnhGUeJo65du0O.modl) (1.7 MB) Automatic publishing has been enabled for the 8.3/main branch and module downloads are now available on …

---

<div class="post-metadata">

**Author:** ![liam1](https://avatars.discourse-cdn.com/v4/letter/l/e68b1a/32.png) [@liam1](https://forum.inductiveautomation.com/u/liam1)\
**Post date:** [August 5, 2025, 6:24am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/68 "2025-08-05T06:24:25Z")

</div>

Thank you.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 7, 2025, 4:16am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/69 "2025-10-07T04:16:06Z")

</div>

Hey @bmusson, it'd be cool if you could add a calendar component to select date ranges into this module 🙂

I added an idea on the github:

> <https://github.com/mussonindustrial/embr/issues/375>
>
> It would be great if a Calendar component could be added which allows selecting …a datetime range in the one component. E.g. Select Start date and then End date, and change start/end times if needed as well, all in the one calendar interface.
> 
> Bonus: highlight "today"!
> Bonus bonus: highlight other days with custom styling, tooltips, icons, based on an array of params.

_Really_, this should be an [idea](https://forum.inductiveautomation.com/t/feature-allow-the-existing-datetime-input-picker-components-to-select-a-date-time-range/111229) for the Ignition codebase itself...

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [October 7, 2025, 7:00am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/70 "2025-10-07T07:00:28Z")

</div>

> **[Getting started | Mantine](https://mantine.dev/dates/getting-started/)**
>
> Get started with @mantine/dates package

Mantine has a good selection of date/time selection components, thoughts?

I could see these being included together in a Mantine components module.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 7, 2025, 8:04am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/71 "2025-10-07T08:04:30Z")

</div>

That library looks really good, I haven't found the date range selection yet, but this looks _ **really** _ useful

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/9/091768c5b87ebdaeb37195a4c9f9a22696539fe2.png)

I found the date range picker, doesn't include time though 🤔:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/f/a/fadf6312ed22be034d134ac570d70337f3f29cea.png)

Looks like a really nice library though!

---

<div class="post-metadata">

**Author:** ![dillon](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dillon/32/81537_2.png) [@dillon](https://forum.inductiveautomation.com/u/dillon)\
**Post date:** [October 7, 2025, 1:00pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/72 "2025-10-07T13:00:49Z")

</div>

> [@nminchin](#):
>
> doesn't include time though

I think using the combination of the date and [the time picker component they have](https://mantine.dev/dates/time-picker/) would be more than enough for my use cases. The [time grid](https://mantine.dev/dates/time-grid/) also interests me!

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [October 31, 2025, 3:02am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/73 "2025-10-31T03:02:18Z")

</div>

## Release Notice

[Embr v8.1 - 2025.10.31](https://github.com/mussonindustrial/embr/releases/tag/releases%2F8.1%2F2025.10.31)  
[Embr v8.3 - 2025.10.31](https://github.com/mussonindustrial/embr/releases/tag/releases%2F8.3%2F2025.10.31)

### Minor Changes

- [3a45993](https://github.com/mussonindustrial/embr/commit/3a45993b15ca8eae8dcb629d8f5fe1f8dc486a35): Modules no longer include `kotlin-stdlib`.
  - All modules now rely on the Kotlin standard library provided with Ignition.

- [7af46dd](https://github.com/mussonindustrial/embr/commit/7af46dd9abdc0e350258e700839aee653ab2a040): (`Flex Repeater +`): Clear non-running `ViewModel` instances from cache on access.

### Patch Changes

- [c7b1b13](https://github.com/mussonindustrial/embr/commit/c7b1b1362e1052cce9916c91f079d8ca9bc01bab): (`Flex Repeater +`): Fix a bug causing the creation of extra ViewModels for non-existent view instances.
- Updated dependencies [[59bafb8](https://github.com/mussonindustrial/embr/commit/59bafb8484b8bc738f0165d303601d6989e91ab3)]
  - @embr-jvm/core-common@0.7.1
  - @embr-jvm/core-designer@0.7.1
  - @embr-modules/periscope-web@0.10.0

---

<div class="post-metadata">

**Author:** ![Aiasa21](https://avatars.discourse-cdn.com/v4/letter/a/aeb1de/32.png) [@Aiasa21](https://forum.inductiveautomation.com/u/Aiasa21)\
**Post date:** [November 29, 2025, 7:25am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/74 "2025-11-29T07:25:22Z")

</div>

> [@bmusson](#):
>
> Assigning a `pattern` to an input text field (this example assigns a pattern that requires a PIN of exactly 4 numbers.

Good morning,  
I’m trying to understand whether the `Portal` component is what I need for the project I’m working on.

At the moment I have about 20 numericFields that need to be filled in. For each field I have 3 bits that indicate whether no data has been entered, whether the value is below the minimum range, or above the maximum range. Based on these flags, I currently change the border color of each numericField.

I’m struggling to understand whether this component is actually what I need to improve the user experience.

For example, step by step, my first question (aside from getting it to work at all) is this: do I need to create one Portal component for each numericField?

A second question: where can I find a working example to start from?

Thanks for the support, and compliments on the work!

---

<div class="post-metadata">

**Author:** ![Aiasa21](https://avatars.discourse-cdn.com/v4/letter/a/aeb1de/32.png) [@Aiasa21](https://forum.inductiveautomation.com/u/Aiasa21)\
**Post date:** [January 7, 2026, 7:08am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/75 "2026-01-07T07:08:09Z")

</div>

> [@bmusson](#):
>
> ## Creating a Toast
> 
> These are client-side toasts, therefore all the interaction is done via [`system.perspective.runJavaScriptAsync`](https://docs.mussonindustrial.com/ignition/embr-periscope/scripting/perspective/system-perspective-runJavaScriptAsync).
> 
> `periscope.toast` is directly mapped to `react-toastify`'s `toast` object, all features are supported.
> 
> ### Simplest Example
> 
> ```python
> system.perspective.runJavaScriptAsync('''() => {
> periscope.toast('Your first toast!')
> }''')
> 
> ```

Hi everyone,

I’m currently using the the periscope module and I’ve encountered an issue at a plant where the network connection is particularly slow. I’ve started seeing the following error in the gateway logs:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/9/0/903798df797b42c8732df26b03aac9a3b42ddc81.png)

After some troubleshooting, I’ve isolated the cause: the error is directly related to the use of the toast object. If I exclude it, the logs remain clean and the errors disappear immediately.

Has anyone else experienced similar issues with high-latency connections? Do you have any advice on how to manage these notifications or configure the object's timeout to prevent the gateway from triggering errors on low-performance lines?

Thanks in advance for your help!

---

<div class="post-metadata">

**Author:** ![David\_Stone](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/david_stone/32/79749_2.png) [@David\_Stone](https://forum.inductiveautomation.com/u/David_Stone)\
**Post date:** [January 7, 2026, 7:27am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/76 "2026-01-07T07:27:07Z")

</div>

On the browser, are there any errors in your developer console?

Looking at the Periscope code, I can see a related timeout with a 30 Second duration... How slow a network are we talking?

This seems more like a failed javascript task on the client not responding, want to share the javascript youre running?

---

<div class="post-metadata">

**Author:** ![Aiasa21](https://avatars.discourse-cdn.com/v4/letter/a/aeb1de/32.png) [@Aiasa21](https://forum.inductiveautomation.com/u/Aiasa21)\
**Post date:** [January 7, 2026, 7:46am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/77 "2026-01-07T07:46:38Z")

</div>

Thanks for the help!

I've tried reproducing the issue via a remote connection, but everything seems to work fine for me right now, so I don't have the console logs yet. I'm going to check later if it's a machine-specific problem.

One thing to note: latency is quite high, with a ping of about 300ms.

This is the function I'm using:

```python
def toast_message(text, locale, position="top-center", toast_type="info", auto_close=5000):

translated_text = translate_message(text, locale)

options = {
    "type": toast_type,
    "position": position,
    "autoClose": auto_close,
    "hideProgressBar": False,
    "newestOnTop": False,
    "closeOnClick": True,
    "rtl": False,
    "pauseOnFocusLoss": False,
    "pauseOnHover": False,
    "theme": "colored"
}

if toast_type == "success":
    options["style"] = { "background": "#1F7A1F", "color": "#FFFFFF" }

json_options = system.util.jsonEncode(options)
json_text = system.util.jsonEncode(translated_text)

return "() => { periscope.toast(%s, %s); }" % (json_text, json_options)

```

And I’m calling it like this:

```python
system.perspective.runJavaScriptAsync(myFuncPath.toast_message(text= "myText", locale = locale, toast_type = "success"))

```

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [January 7, 2026, 12:58pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/78 "2026-01-07T12:58:19Z")

</div>

Don’t do string substitution into the function body, instead pass arguments for both your translated text and style. This section of the documentation bas an example:

> **[system.perspective.runJavaScriptAsync | Musson Industrial](https://docs.mussonindustrial.com/ignition/embr-periscope/scripting/perspective/system-perspective-runJavaScriptAsync/)**
>
> The system.perspective.runJavaScriptAsync function allows you to asynchronously run JavaScript code on the client side within a Perspective session.

(Copied here for reference)

```python

# Function with three parameters.
function = '''(param1, param2, param3) => {
    return param1 + param2 + param3.value;
}'''

def callback(result):
    system.perspective.print(result)

# The keys of the arguments dictionary are mapped to the function argument names.
args = {
    'param1': 10,
    'param2': 20,
    'param3': {
        'value': 30
    }
}

system.perspective.runJavaScriptAsync(function, args, callback)

```

But I do agree that this kinda looks like a network related issue.

---

<div class="post-metadata">

**Author:** ![Aiasa21](https://avatars.discourse-cdn.com/v4/letter/a/aeb1de/32.png) [@Aiasa21](https://forum.inductiveautomation.com/u/Aiasa21)\
**Post date:** [January 7, 2026, 3:55pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/79 "2026-01-07T15:55:59Z")

</div>

I’ve modified the code passing arguments, thanks for the suggestion.

Is there something I can change to avoid the error? Increase the timeout in some way for example?

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [January 7, 2026, 4:12pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/80 "2026-01-07T16:12:14Z")

</div>

No, the timeout is hardcoded.

> <https://github.com/mussonindustrial/embr/blob/8.3/main/modules/periscope/gateway/src/main/kotlin/com/mussonindustrial/ignition/embr/periscope/scripting/JavaScriptFunctions.kt#L38C5-L38C30>

If it is taking longer 30 seconds something is really wrong.

And there are no other errors in the gateway or the client?

---

<div class="post-metadata">

**Author:** ![Aiasa21](https://avatars.discourse-cdn.com/v4/letter/a/aeb1de/32.png) [@Aiasa21](https://forum.inductiveautomation.com/u/Aiasa21)\
**Post date:** [January 8, 2026, 7:31am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/81 "2026-01-08T07:31:50Z")

</div>

Actually, I don’t understand why it should take so long. I tried connecting to one PC and yes, it is a little bit slow, but it's not unusable.

At the moment, I have disabled the toast notifications for that specific plant. In the meantime, I am trying to dig deeper to see if it is a specific PC creating this problem.

Thanks for the support and also for the resources, they’re amazing.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 3, 2026, 8:15am UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/82 "2026-03-03T08:15:12Z")

</div>

# Release Notice

[Embr v8.1 - 2026.3.3](https://github.com/mussonindustrial/embr/releases/tag/releases%2F8.1%2F2026.3.3)  
[Embr v8.3 - 2026.3.3](https://github.com/mussonindustrial/embr/releases/tag/releases%2F8.3%2F2026.3.3)

This release includes a scripting function for submitting functions to a Perspective session's queue:

`system.perspective.invokeOnQueue(function, [delay], [scope], [sessionId], [pageId])`

#### Parameters

- **function** `(required)`  
A function that will be called with a single parameter:

- **delay** `(optional)`  
Time in milliseconds to wait before invoking the function.

- **scope** `(optional)`  
Controls the lifecycle of the function relative to Perspective objects. Default is `'view'`.

- **sessionId** `(optional)`  
The target session ID.

- **pageId** `(optional)`  
The target page ID.

## Examples

### Delayed Execution

```python
def myFunction(scope):
    system.perspective.print("Running after 5 seconds. Neat.")

system.perspective.invokeOnQueue(myFunction, 5000) # default scope='view'

```

### Incrementing Counter

This example counts to 50 using view custom properties.

```python
def doThing(scope):
	if self.view.custom.count < 50:
		self.view.custom.count += 1
		self.view.custom.text = 'Counting: %d' % self.view.custom.count
		system.perspective.invokeOnQueue(doThing, 100)
	else:
		self.view.custom.text = "Done!"
	
self.view.custom.count = 0
self.view.custom.text = "Waiting to start...."
system.perspective.invokeOnQueue(doThing, 1000)

```

### Incrementing Counters (across all sessions)

This example counts to 50 using session custom properties. The functions will be run across all client sessions using `scope='session'` and `sessionId` parameters.

```python
def makeHandlers(sessionId):
    def doThing(scope):
        if scope.custom.count < 50:
            scope.custom.count += 1
            scope.custom.text = scope.custom.count
            system.perspective.invokeOnQueue(doThing, 100, scope='session', sessionId=sessionId)
        else:
            scope.custom.text = "Done!"
	
    def setup(scope):
        scope.custom.count = 0
        scope.custom.text = "Waiting to start...."
        system.perspective.invokeOnQueue(doThing, scope='session', sessionId=sessionId)
	
    return setup
	
	
sessions = system.perspective.getSessionInfo()
	
for session in sessions:
	# Simple way to filter out designer sessions
    if len(session.id) < 36:
        continue
	
    setup = makeHandlers(session.id)
    system.perspective.invokeOnQueue(setup, 1000, scope='session', sessionId=session.id)

```

## New Sponsors

Big thanks to both @David_Stone and @andrew.budaiev for sponsoring this project!  
If you're interested in contributing, check us out on [GitHub Sponsors.](https://github.com/sponsors/mussonindustrial)

[Previous page](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047.md?page=3)

[Next page](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047.md?page=5)
