mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-03-28 17:42:56 +01:00
Initial commit
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
"""add lat lon columns to nodes
|
||||||
|
|
||||||
|
Revision ID: 4e2e787a1660
|
||||||
|
Revises: aa1162502616
|
||||||
|
Create Date: 2026-01-09 20:04:04.273741+00:00
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "4e2e787a1660"
|
||||||
|
down_revision: Union[str, None] = "aa1162502616"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table("nodes", schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column("lat", sa.Float(), nullable=True))
|
||||||
|
batch_op.add_column(sa.Column("lon", sa.Float(), nullable=True))
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table("nodes", schema=None) as batch_op:
|
||||||
|
batch_op.drop_column("lon")
|
||||||
|
batch_op.drop_column("lat")
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -47,6 +47,10 @@ def handle_contact(
|
|||||||
# Device uses 'adv_name' for the advertised name
|
# Device uses 'adv_name' for the advertised name
|
||||||
name = payload.get("adv_name") or payload.get("name")
|
name = payload.get("adv_name") or payload.get("name")
|
||||||
|
|
||||||
|
# GPS coordinates (optional)
|
||||||
|
lat = payload.get("adv_lat")
|
||||||
|
lon = payload.get("adv_lon")
|
||||||
|
|
||||||
logger.info(f"Processing contact: {contact_key[:12]}... adv_name={name}")
|
logger.info(f"Processing contact: {contact_key[:12]}... adv_name={name}")
|
||||||
|
|
||||||
# Device uses numeric 'type' field, convert to string
|
# Device uses numeric 'type' field, convert to string
|
||||||
@@ -73,6 +77,11 @@ def handle_contact(
|
|||||||
node.name = name
|
node.name = name
|
||||||
if node_type and not node.adv_type:
|
if node_type and not node.adv_type:
|
||||||
node.adv_type = node_type
|
node.adv_type = node_type
|
||||||
|
# Update GPS coordinates if provided
|
||||||
|
if lat is not None:
|
||||||
|
node.lat = lat
|
||||||
|
if lon is not None:
|
||||||
|
node.lon = lon
|
||||||
# Do NOT update last_seen for contact sync - only advertisement events
|
# Do NOT update last_seen for contact sync - only advertisement events
|
||||||
# should update last_seen since that's when the node was actually seen
|
# should update last_seen since that's when the node was actually seen
|
||||||
else:
|
else:
|
||||||
@@ -84,6 +93,8 @@ def handle_contact(
|
|||||||
adv_type=node_type,
|
adv_type=node_type,
|
||||||
first_seen=now,
|
first_seen=now,
|
||||||
last_seen=None, # Will be set when we receive an advertisement
|
last_seen=None, # Will be set when we receive an advertisement
|
||||||
|
lat=lat,
|
||||||
|
lon=lon,
|
||||||
)
|
)
|
||||||
session.add(node)
|
session.add(node)
|
||||||
logger.info(f"Created node from contact: {contact_key[:12]}... ({name})")
|
logger.info(f"Created node from contact: {contact_key[:12]}... ({name})")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from sqlalchemy import DateTime, Index, Integer, String
|
from sqlalchemy import DateTime, Float, Index, Integer, String
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
from meshcore_hub.common.models.base import Base, TimestampMixin, UUIDMixin, utc_now
|
from meshcore_hub.common.models.base import Base, TimestampMixin, UUIDMixin, utc_now
|
||||||
@@ -23,6 +23,8 @@ class Node(Base, UUIDMixin, TimestampMixin):
|
|||||||
flags: Capability/status flags bitmask
|
flags: Capability/status flags bitmask
|
||||||
first_seen: Timestamp of first advertisement
|
first_seen: Timestamp of first advertisement
|
||||||
last_seen: Timestamp of most recent activity
|
last_seen: Timestamp of most recent activity
|
||||||
|
lat: GPS latitude coordinate (if available)
|
||||||
|
lon: GPS longitude coordinate (if available)
|
||||||
created_at: Record creation timestamp
|
created_at: Record creation timestamp
|
||||||
updated_at: Record update timestamp
|
updated_at: Record update timestamp
|
||||||
"""
|
"""
|
||||||
@@ -57,6 +59,14 @@ class Node(Base, UUIDMixin, TimestampMixin):
|
|||||||
default=None,
|
default=None,
|
||||||
nullable=True,
|
nullable=True,
|
||||||
)
|
)
|
||||||
|
lat: Mapped[Optional[float]] = mapped_column(
|
||||||
|
Float,
|
||||||
|
nullable=True,
|
||||||
|
)
|
||||||
|
lon: Mapped[Optional[float]] = mapped_column(
|
||||||
|
Float,
|
||||||
|
nullable=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
tags: Mapped[list["NodeTag"]] = relationship(
|
tags: Mapped[list["NodeTag"]] = relationship(
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ class NodeRead(BaseModel):
|
|||||||
last_seen: Optional[datetime] = Field(
|
last_seen: Optional[datetime] = Field(
|
||||||
default=None, description="Last activity timestamp"
|
default=None, description="Last activity timestamp"
|
||||||
)
|
)
|
||||||
|
lat: Optional[float] = Field(default=None, description="GPS latitude coordinate")
|
||||||
|
lon: Optional[float] = Field(default=None, description="GPS longitude coordinate")
|
||||||
created_at: datetime = Field(..., description="Record creation timestamp")
|
created_at: datetime = Field(..., description="Record creation timestamp")
|
||||||
updated_at: datetime = Field(..., description="Record update timestamp")
|
updated_at: datetime = Field(..., description="Record update timestamp")
|
||||||
tags: list[NodeTagRead] = Field(default_factory=list, description="Node tags")
|
tags: list[NodeTagRead] = Field(default_factory=list, description="Node tags")
|
||||||
|
|||||||
Reference in New Issue
Block a user