Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions flows/modem-manager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
## modem-manager

Publish modem manager (cellular) information including cell tower id which can be used for tracking.

### Description

- Detect changes of the cell tower id
- Send data periodically - configurable interval

The flow is represented by the following steps:

- Detect changes in the cell tower id, and send an event to the cloud to determine location information
- Publish c8y_Mobile information about the modem (using modem manager data)

### Input

The flow expects the modem manager input json in the following format:

**JSON payload**

```json
{
"sim": {
"dbus-path": "/org/freedesktop/ModemManager1/SIM/0",
"properties": {
"active": "yes",
"eid": "--",
"emergency-numbers": [],
"esim-status": "--",
"gid1": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"gid2": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"iccid": "8949360000421790514",
"imsi": "262231017669630",
"operator-code": "26223",
"operator-name": "1&1",
"preferred-networks": [
"operator-code: 26223, access-technologies: lte",
"operator-code: 20801, access-technologies: gsm, umts, lte"
],
"removability": "--",
"sim-type": "--"
}
},
"modem": {
"location": {
"3gpp": {
"cid": "00A49109",
"lac": "0000",
"mcc": "262",
"mnc": "02",
"tac": "00AD2B"
},
"cdma-bs": {
"latitude": "--",
"longitude": "--"
},
"gps": {
"altitude": "--",
"latitude": "--",
"longitude": "--",
"nmea": [],
"utc": "--"
}
},
"3gpp": {
"5gnr": {
"registration-settings": {
"drx-cycle": "--",
"mico-mode": "--"
}
},
"enabled-locks": ["fixed-dialing"],
"eps": {
"initial-bearer": {
"dbus-path": "/org/freedesktop/ModemManager1/Bearer/1",
"settings": {
"apn": "--",
"ip-type": "--",
"password": "--",
"user": "--"
}
},
"ue-mode-operation": "csps-1"
},
"imei": "860018046114881",
"network-rejection-access-technology": "--",
"network-rejection-error": "--",
"network-rejection-operator-id": "--",
"network-rejection-operator-name": "--",
"operator-code": "26202",
"operator-name": "Vodafone.de",
"packet-service-state": "attached",
"pco": "--",
"registration-state": "home"
},
"cdma": {
"activation-state": "--",
"cdma1x-registration-state": "--",
"esn": "--",
"evdo-registration-state": "--",
"meid": "--",
"nid": "--",
"sid": "--"
},
"dbus-path": "/org/freedesktop/ModemManager1/Modem/0",
"generic": {
"access-technologies": ["lte"],
"bearers": ["/org/freedesktop/ModemManager1/Bearer/0"],
"carrier-configuration": "--",
"carrier-configuration-revision": "--",
"current-bands": [
"utran-1",
"utran-8",
"eutran-1",
"eutran-3",
"eutran-5",
"eutran-7",
"eutran-8",
"eutran-20",
"eutran-38",
"eutran-40",
"eutran-41"
],
"current-capabilities": ["gsm-umts, lte"],
"current-modes": "allowed: 3g, 4g; preferred: 3g",
"device": "qcom-soc",
"device-identifier": "69bc305f94b9e998c1f05afae06af3eb2f806829",
"drivers": ["bam-dmux", "qcom-q6v5-mss", "rpmsg_ctrl"],
"equipment-identifier": "860018046114881",
"hardware-revision": "10000",
"manufacturer": "1",
"model": "0",
"own-numbers": [],
"physdev": "/sys/devices/platform/soc@0/4080000.remoteproc/4080000.remoteproc:bam-dmux",
"plugin": "qcom-soc",
"ports": [
"rpmsg_ctrl2 (ignored)",
"wwan0 (net)",
"wwan0at0 (at)",
"wwan0at1 (at)",
"wwan0qmi0 (qmi)"
],
"power-state": "on",
"primary-port": "wwan0qmi0",
"primary-sim-slot": "1",
"revision": "UZ801_V3.0_21_V01R01B10 1 [Sep 07 2015 23:00:00]",
"signal-quality": {
"recent": "yes",
"value": "80"
},
"sim": "/org/freedesktop/ModemManager1/SIM/0",
"sim-slots": ["/org/freedesktop/ModemManager1/SIM/0", "/"],
"state": "connected",
"state-failed-reason": "--",
"supported-bands": [
"utran-1",
"utran-8",
"eutran-1",
"eutran-3",
"eutran-5",
"eutran-7",
"eutran-8",
"eutran-20",
"eutran-38",
"eutran-40",
"eutran-41"
],
"supported-capabilities": ["gsm-umts, lte"],
"supported-ip-families": ["ipv4", "ipv6", "ipv4v6"],
"supported-modes": [
"allowed: 3g; preferred: none",
"allowed: 4g; preferred: none",
"allowed: 3g, 4g; preferred: 4g",
"allowed: 3g, 4g; preferred: 3g"
],
"unlock-required": "sim-pin2",
"unlock-retries": [
"sim-pin (3)",
"sim-puk (10)",
"sim-pin2 (3)",
"sim-puk2 (10)"
]
}
}
}
```
7 changes: 7 additions & 0 deletions flows/modem-manager/flow.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[input]
mqtt.topics = [
"te/device/main///source/modemManager",
]

[[steps]]
script = "dist/main.mjs"
19 changes: 19 additions & 0 deletions flows/modem-manager/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/** @jest-config-loader esbuild-register */
import type { Config } from "jest";

const esbuildOptions = {
target: "es2018",
// ignore labels for test coverage
dropLabels: ["TEST", "DEV"],
};

const config: Config = {
verbose: true,
transform: { "^.+\\.ts?$": ["jest-esbuild", esbuildOptions] },
testEnvironment: "node",
testRegex: "/tests/.*\\.(test|spec)?\\.(ts|tsx)$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
coverageReporters: ["clover", "json", "lcov", ["text", { skipFull: true }]],
};

export default config;
23 changes: 23 additions & 0 deletions flows/modem-manager/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "modem-manager",
"version": "1.0.0",
"description": "Publish meta information about the modem as digital twin and send an alert when the cell id has changed",
"source": "src/main.ts",
"module": "dist/main.mjs",
"type": "module",
"scripts": {
"build": "esbuild src/main.ts --target=es2018 --bundle --outfile=dist/main.mjs --format=esm --drop-labels=DEV,TEST",
"test": "jest --coverageProvider=v8 --coverage"
},
"author": "thin-edge.io",
"license": "Apache-2.0",
"devDependencies": {
"@types/jest": "^30.0.0",
"esbuild": "^0.25.5",
"esbuild-register": "^3.6.0",
"jest": "^30.0.4",
"jest-esbuild": "^0.4.0",
"prettier": "3.6.2",
"typescript": "^5.8.3"
}
}
124 changes: 124 additions & 0 deletions flows/modem-manager/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
Publish modem information
*/
import { Message } from "../../common/tedge";

export interface Config {
interval_sec?: number;
}

export interface Mobile {
currentOperator?: String;
iccid?: String;
cellId?: String;
imei?: String;
imsi?: String;
mcc?: String;
mnc?: String;
lac?: String;
connType?: String;
signalQuality?: Number;
}

export interface State {
mobile: Mobile;
}

const state: State = {
mobile: {},
};

function hasChanged(obj1, obj2): boolean {
return JSON.stringify(obj1) !== JSON.stringify(obj2);
}

export function onMessage(message: Message, config: Config): Message[] {
const payload = JSON.parse(message.payload);
const output: Message[] = [];

const mobile: Mobile = {};

// sim details
if (payload.hasOwnProperty("sim")) {
mobile.iccid = payload?.sim?.properties?.iccid;
mobile.imsi = payload?.sim?.properties?.imsi;
}

// modem details
if (
payload.hasOwnProperty("modem") &&
payload["modem"].hasOwnProperty("3gpp")
) {
mobile.currentOperator = payload["modem"]["3gpp"]["operator-name"];
mobile.imei = payload["modem"]["3gpp"]["imei"];
}

mobile.connType = (payload?.modem?.generic["access-technologies"] || []).join(
",",
);

// signalQuality
const signalQualityRecent = payload?.modem?.generic["signal-quality"].recent;
const signalQualityPercentageRaw =
payload?.modem?.generic["signal-quality"].value;
const signalQualityPercentage = parseInt(signalQualityPercentageRaw, 10);
if (isFinite(signalQualityPercentage)) {
mobile.signalQuality = signalQualityPercentage;
}

// location details
if (
payload.hasOwnProperty("modem") &&
payload["modem"].hasOwnProperty("location")
) {
const location = payload["modem"]["location"];
mobile.cellId = Number(`0x${location["3gpp"]["cid"]}`).toFixed(0);
mobile.mcc = location["3gpp"]["mcc"];
mobile.mnc = location["3gpp"]["mnc"];

const locationAreaCode = Number(`0x${location["3gpp"]["lac"]}`);
const trackingAreaCode = Number(`0x${location["3gpp"]["tac"]}`);
if (locationAreaCode === 0) {
mobile.lac = trackingAreaCode.toFixed(0);
} else {
mobile.lac = locationAreaCode.toFixed(0);
}
}

if (hasChanged(mobile, state.mobile)) {
output.push({
topic: "te/device/main///twin/c8y_Mobile",
retain: true,
payload: JSON.stringify({
...mobile,
lastChanged: new Date(message.timestamp.seconds * 1000).toISOString(),
}),
timestamp: message.timestamp,
});
}

// cell tower change detection
if (!!state.mobile.cellId && state.mobile.cellId !== mobile.cellId) {
output.push({
topic: "te/device/main///e/cellTowerDetails",
payload: JSON.stringify({
text: "Cell tower changed",
previousCellTowerDetails: {
mcc: state.mobile.mcc,
mnc: state.mobile.mnc,
lac: state.mobile.lac,
},
cellTowerDetails: {
mcc: mobile.mcc,
mnc: mobile.mnc,
lac: mobile.lac,
},
}),
timestamp: message.timestamp,
});
}

state.mobile = mobile;

return output;
}
24 changes: 24 additions & 0 deletions flows/modem-manager/tests/data/location.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"modem": {
"location": {
"3gpp": {
"cid": "00A49109",
"lac": "0000",
"mcc": "262",
"mnc": "02",
"tac": "00AD2B"
},
"cdma-bs": {
"latitude": "--",
"longitude": "--"
},
"gps": {
"altitude": "--",
"latitude": "--",
"longitude": "--",
"nmea": [],
"utc": "--"
}
}
}
}
Loading